diff options
author | Lars Amsel <lars.amsel@ni.com> | 2021-06-04 08:27:50 +0200 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-06-10 12:01:53 -0500 |
commit | 2a575bf9b5a4942f60e979161764b9e942699e1e (patch) | |
tree | 2f0535625c30025559ebd7494a4b9e7122550a73 /mpm | |
parent | e17916220cc955fa219ae37f607626ba88c4afe3 (diff) | |
download | uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.tar.gz uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.tar.bz2 uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.zip |
uhd: Add support for the USRP X410
Co-authored-by: Lars Amsel <lars.amsel@ni.com>
Co-authored-by: Michael Auchter <michael.auchter@ni.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Cristina Fuentes <cristina.fuentes-curiel@ni.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Virendra Kakade <virendra.kakade@ni.com>
Co-authored-by: Lane Kolbly <lane.kolbly@ni.com>
Co-authored-by: Max Köhler <max.koehler@ni.com>
Co-authored-by: Andrew Lynch <andrew.lynch@ni.com>
Co-authored-by: Grant Meyerhoff <grant.meyerhoff@ni.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Thomas Vogel <thomas.vogel@ni.com>
Diffstat (limited to 'mpm')
82 files changed, 28524 insertions, 93 deletions
diff --git a/mpm/CMakeLists.txt b/mpm/CMakeLists.txt index 8dfd7da6c..731e2ba13 100644 --- a/mpm/CMakeLists.txt +++ b/mpm/CMakeLists.txt @@ -128,7 +128,7 @@ if(_has_no_psabi) message(STATUS "Disabling psABI warnings.") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi") endif(_has_no_psabi) -set(MPM_ALL_DEVICES n3xx e320 e31x sim tests) +set(MPM_ALL_DEVICES n3xx e320 e31x x4xx sim tests) set(MPM_DEVICE "n3xx" CACHE STRING "Choose an MPM device to build") set_property(CACHE MPM_DEVICE PROPERTY STRINGS ${MPM_ALL_DEVICES}) # Validate MPM_DEVICE @@ -146,6 +146,9 @@ elseif(MPM_DEVICE STREQUAL "e320") set(ENABLE_E320 ON) elseif(MPM_DEVICE STREQUAL "e31x") set(ENABLE_E300 ON) +elseif(MPM_DEVICE STREQUAL "x4xx") + set(ENABLE_X400 ON) + set(DEVICE_LIBRARIES "metal") elseif(MPM_DEVICE STREQUAL "sim") set(ENABLE_SIM TRUE) set(ENABLE_LIBMPM OFF) @@ -157,6 +160,7 @@ MPM_REGISTER_COMPONENT("Mykonos" ENABLE_MYKONOS OFF "ENABLE_LIBMPM" OFF OFF) MPM_REGISTER_COMPONENT("Magnesium" ENABLE_MAGNESIUM OFF "ENABLE_MYKONOS" OFF OFF) MPM_REGISTER_COMPONENT("E320" ENABLE_E320 OFF "ENABLE_LIBMPM" OFF OFF) MPM_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBMPM" OFF OFF) +MPM_REGISTER_COMPONENT("X400" ENABLE_X400 OFF "ENABLE_LIBMPM;ENABLE_REGMAPS" OFF OFF) if(NOT ENABLE_SIM) add_subdirectory(include) @@ -173,6 +177,7 @@ if(NOT ENABLE_SIM) target_link_libraries(usrp-periphs udev ${Boost_LIBRARIES} + ${DEVICE_LIBRARIES} ) install(TARGETS usrp-periphs LIBRARY DESTINATION ${LIBRARY_DIR} COMPONENT libraries) diff --git a/mpm/include/mpm/CMakeLists.txt b/mpm/include/mpm/CMakeLists.txt index d4caff1c4..4a5a3acc2 100644 --- a/mpm/include/mpm/CMakeLists.txt +++ b/mpm/include/mpm/CMakeLists.txt @@ -18,3 +18,7 @@ add_subdirectory(chips) add_subdirectory(dboards) add_subdirectory(spi) add_subdirectory(types) + +if(ENABLE_X400) + add_subdirectory(rfdc) +endif(ENABLE_X400) diff --git a/mpm/include/mpm/i2c/i2c_iface.hpp b/mpm/include/mpm/i2c/i2c_iface.hpp index c49a70b48..02dc23acf 100644 --- a/mpm/include/mpm/i2c/i2c_iface.hpp +++ b/mpm/include/mpm/i2c/i2c_iface.hpp @@ -43,16 +43,17 @@ public: /*! * \param tx Buffer of data to send - * \param rx Buffer to hold read data + * \param rx_num_bytes Number of bytes to read into rx return buffer * \param do_close If true, close file descriptor at end of function + * \return Buffer of rx data * * All data in tx will be transmitted. - * The amount of data read will be determined by the number of elements - * in the rx vector. Those elements will be overwritten with the data. - * Use the resize() function for a new rx vector. + * The amount of data read will be determined by num_rx_bytes and written + * into the return buffer. */ - virtual int transfer( - std::vector<uint8_t>* tx, std::vector<uint8_t>* rx, bool do_close = true) = 0; + virtual std::vector<uint8_t> transfer( + std::vector<uint8_t>& tx, size_t num_rx_bytes, bool do_close = true) = 0; + }; }}; /* namespace mpm::i2c */ diff --git a/mpm/include/mpm/i2c/i2c_python.hpp b/mpm/include/mpm/i2c/i2c_python.hpp index ebc9cb0a1..18209adf2 100644 --- a/mpm/include/mpm/i2c/i2c_python.hpp +++ b/mpm/include/mpm/i2c/i2c_python.hpp @@ -14,5 +14,9 @@ void export_i2c(py::module& top_module) { auto m = top_module.def_submodule("i2c"); + m.def("make_i2cdev", &mpm::i2c::i2c_iface::make_i2cdev); m.def("make_i2cdev_regs_iface", &mpm::i2c::make_i2cdev_regs_iface); + + py::class_<mpm::i2c::i2c_iface, std::shared_ptr<mpm::i2c::i2c_iface>>(m, "i2c_iface") + .def("transfer", (std::vector<uint8_t> (mpm::i2c::i2c_iface::*)(std::vector<uint8_t>&, size_t, bool)) &mpm::i2c::i2c_iface::transfer, "Transfer i2c data"); } diff --git a/mpm/include/mpm/rfdc/CMakeLists.txt b/mpm/include/mpm/rfdc/CMakeLists.txt new file mode 100644 index 000000000..cbd1ce5bc --- /dev/null +++ b/mpm/include/mpm/rfdc/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Copyright 2019 Ettus Research, National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0 +# +install(FILES + rfdc_ctrl.hpp + rfdc_throw.h + xrfdc_hw.h + xrfdc_mts.h + xrfdc.h + DESTINATION ${INCLUDE_DIR}/mpm/rfdc +) diff --git a/mpm/include/mpm/rfdc/rfdc_ctrl.hpp b/mpm/include/mpm/rfdc/rfdc_ctrl.hpp new file mode 100644 index 000000000..411b70c90 --- /dev/null +++ b/mpm/include/mpm/rfdc/rfdc_ctrl.hpp @@ -0,0 +1,755 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include "xrfdc.h" +#include "xrfdc_mts.h" +#include <boost/noncopyable.hpp> +#include <vector> + +#ifdef LIBMPM_PYTHON +# include <pybind11/stl.h> +#endif + +#define THRESHOLDS_PER_BLOCK 2 + +namespace mpm { namespace rfdc { + +/** + * A class to control the Xilinx RFdc driver. + * This will be imported into a MPM shared library. + */ +class rfdc_ctrl : public boost::noncopyable +{ + XRFdc rfdc_inst; + XRFdc* rfdc_inst_ptr; + XRFdc_MultiConverter_Sync_Config rfdc_dac_sync_config; + XRFdc_MultiConverter_Sync_Config rfdc_adc_sync_config; + uint16_t rfdc_device_id; + +public: + /** + * These macros are placed within enums so they can be imported to Python. + * They are originally defined in xrfdc.h + */ + enum threshold_id_options { + THRESHOLD_0 = XRFDC_UPDATE_THRESHOLD_0, + THRESHOLD_1 = XRFDC_UPDATE_THRESHOLD_1, + THRESHOLD_BOTH = XRFDC_UPDATE_THRESHOLD_BOTH + }; + enum threshold_mode_options { + TRSHD_OFF = XRFDC_TRSHD_OFF, + TRSHD_STICKY_OVER = XRFDC_TRSHD_STICKY_OVER, + TRSHD_STICKY_UNDER = XRFDC_TRSHD_STICKY_UNDER, + TRSHD_HYSTERESIS = XRFDC_TRSHD_HYSTERISIS + }; + enum threshold_clr_mode_options { + THRESHOLD_CLRMD_MANUAL = XRFDC_THRESHOLD_CLRMD_MANUAL_CLR, + THRESHOLD_CLRMD_AUTO = XRFDC_THRESHOLD_CLRMD_AUTO_CLR, + // The XRFdc Threshold clear modes currently only go up to 2 + // This assumes there will never be a clear mode of value 99 + THRESHOLD_CLRMD_UNKNOWN = 99 + }; + enum decoder_mode_options { + DECODER_MAX_SNR_MODE = XRFDC_DECODER_MAX_SNR_MODE, // for non-randomized decoder + DECODER_MAX_LINEARITY_MODE = + XRFDC_DECODER_MAX_LINEARITY_MODE // for randomized decoder + }; + enum nyquist_zone_options { + ODD_NYQUIST_ZONE = XRFDC_ODD_NYQUIST_ZONE, + EVEN_NYQUIST_ZONE = XRFDC_EVEN_NYQUIST_ZONE + }; + enum mixer_mode_options { + MIXER_MODE_OFF = XRFDC_MIXER_MODE_OFF, + MIXER_MODE_C2C = XRFDC_MIXER_MODE_C2C, // Complex to complex + MIXER_MODE_C2R = XRFDC_MIXER_MODE_C2R, // Complex to real + MIXER_MODE_R2C = XRFDC_MIXER_MODE_R2C, // Real to complex + MIXER_MODE_R2R = XRFDC_MIXER_MODE_R2R // Real to real + }; + /** + * See section "RF-ADC Settings" of the Xilinx + * "RF Data Converter Interface User Guide" to learn + * more about the calibration modes. + */ + enum calibration_mode_options { + CALIB_MODE1 = XRFDC_CALIB_MODE1, + CALIB_MODE2 = XRFDC_CALIB_MODE2 + }; + enum event_type_options { + MIXER_EVENT = XRFDC_EVENT_MIXER, + CRSE_DLY_EVENT = XRFDC_EVENT_CRSE_DLY, + QMC_EVENT = XRFDC_EVENT_QMC, + }; + enum interp_decim_options { + INTERP_DECIM_OFF = XRFDC_INTERP_DECIM_OFF, + INTERP_DECIM_1X = XRFDC_INTERP_DECIM_1X, + INTERP_DECIM_2X = XRFDC_INTERP_DECIM_2X, + INTERP_DECIM_4X = XRFDC_INTERP_DECIM_4X, + INTERP_DECIM_8X = XRFDC_INTERP_DECIM_8X, + }; + enum fabric_clk_div_options { + DIV_1 = XRFDC_FAB_CLK_DIV1, + DIV_2 = XRFDC_FAB_CLK_DIV2, + DIV_4 = XRFDC_FAB_CLK_DIV4, + DIV_8 = XRFDC_FAB_CLK_DIV8, + DIV_16 = XRFDC_FAB_CLK_DIV16, + }; + + /** + * Assignes the rfdc_inst_ptr to an instance of the Xilinx RFdc driver + */ + rfdc_ctrl(); + + /** + * Closes the libmetal device + */ + ~rfdc_ctrl(); + + /** + * Initializes the driver by reading configuration settings + * from the device found in the device tree and applying them to + * the driver instance. + * Throws an exception if init fails. + * + * @param rfdc_device_id the device ID of the rfdc device + */ + void init(uint16_t rfdc_device_id); + + /** + * Starts up the requested tile while retaining register values. + * + * @param tile_id the ID of the tile to start. + * Pass -1 to select all tiles. + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * + * @return true if the operation was successful + */ + bool startup_tile(int tile_id, bool is_dac); + + /** + * Shuts down the requested tile while retaining register values. + * + * @param tile_id the ID of the tile to stop. + * Pass -1 to select all tiles. + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * + * @return true if the operation was successful + */ + bool shutdown_tile(int tile_id, bool is_dac); + + /** + * Restarts the requested tile while resetting registers to default values. + * + * @param tile_id the ID of the tile to restart. + * Pass -1 to select all tiles. + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * + * @return true if the operation was successful + */ + bool reset_tile(int tile_id, bool is_dac); + + /** + * Triggers an update event for a given component. + * + * @param tile_id the tile ID of the block to trigger + * @param block_id the block ID of the block to trigger + * @param is_dac whether the block is a DAC (true) or ADC (false) + * @param event_type which component of block to update + * See event_type_options for valid values. + * + * @return true if the operation was successful + */ + bool trigger_update_event(uint32_t tile_id, uint32_t block_id, + bool is_dac, event_type_options event_type); + + /** + * Enable/Disable gain correction for a given block. + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param is_dac whether the block is a DAC (true) or ADC (false) + * @param enable whether to enable or disable gain correction + * + * @return true if the operation was successful + */ + bool set_gain_enable(uint32_t tile_id, uint32_t block_id, bool is_dac, bool enable); + + /** + * Set gain correction on a given ADC or DAC block + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param is_dac whether the block is a DAC (true) or ADC (false) + * @param gain the gain correction to set. + * Valid values are 0.0-2.0 + * + * @return true if the operation was successful + */ + bool set_gain(uint32_t tile_id, uint32_t block_id, bool is_dac, double gain); + + /** + * Set the threshold settings for a given ADC + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param threshold_id the ID of the threshold to modify. + * See threshold_id_options for valid values. + * @param mode the threshold mode to set. + * See threshold_mode_options for valid values. + * @param average_val the average threshold value + * @param under_val the under threshold value + * @param over_val the over threshold value + * + * @return true if the operation was successful + */ + bool set_threshold_settings(uint32_t tile_id, + uint32_t block_id, + threshold_id_options threshold_id, + threshold_mode_options mode, + uint32_t average_val, + uint32_t under_val, + uint32_t over_val); + + /** + * Clears the sticky line which indicates a threshold has been breached. + * This will also set the sticky clear mode to be manual. + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param threshold_id the ID of the threshold to modify. + * See threshold_id_options for valid values. + * + * @return true if the operation was successful + */ + bool clear_threshold_sticky( + uint32_t tile_id, uint32_t block_id, threshold_id_options threshold_id); + + /** + * Sets whether the threshold breach sticky is cleared manually + * or automatically (when QMC gain is changed). + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param threshold_id the ID of the threshold to modify. + * See threshold_id_options for valid values. + * @param mode What mode to set for the threshold sticky clear mode. + * See threshold_clr_mode_options for valid values. + * + * @return true if the operation was successful + */ + bool set_threshold_clr_mode(uint32_t tile_id, + uint32_t block_id, + threshold_id_options threshold_id, + threshold_clr_mode_options clear_mode); + + /** + * Gets the threshold sticky clear mode + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param threshold_id the ID of the threshold to modify. + * See threshold_id_options for valid values. + * Note: THRESHOLD_BOTH is not a valid threshold_id for this + * method and will result in THRESHOLD_CLRMD_UNKNOWN. + * @param mode What mode to set for the threshold sticky clear mode. + * See threshold_clr_mode_options for valid values. + * + * @return threshold_clr_mode_options which is currently set. + * A value of THRESHOLD_CLRMD_UNKNOWN indicates that the hardware + * setting is currently unknown or an invalid ID was given. + */ + threshold_clr_mode_options get_threshold_clr_mode( + uint32_t tile_id, uint32_t block_id, threshold_id_options threshold_id); + + /** + * Sets the decoder mode of a given DAC + * An auto-clear takes place when the gain setting is changed. + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param decoder_mode the desired decoder mode for the DAC. + * See decoder_mode_options for valid values. + * + * @return true if the operation was successful + */ + bool set_decoder_mode( + uint32_t tile_id, uint32_t block_id, decoder_mode_options decoder_mode); + + /** + * Resets the NCO phase of the current block phase accumulator. + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param is_dac whether the block is a DAC (true) or ADC (false) + * + * @return true if the operation was successful + */ + bool reset_nco_phase(uint32_t tile_id, uint32_t block_id, bool is_dac); + + /** + * Sets the NCO event source for a given DAC or ADC + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param is_dac whether the block is a DAC (true) or ADC (false) + * + * @return true if the operation was successful + */ + bool set_nco_event_src(uint32_t tile_id, uint32_t block_id, bool is_dac); + + /** + * Sets the NCO frequency for a given DAC or ADC + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param is_dac whether the block is a DAC (true) or ADC (false) + * @param freq the NCO frequency to set + * Frequencies are specified in Hz. + * + * @return true if the operation was successful + */ + bool set_nco_freq(uint32_t tile_id, uint32_t block_id, bool is_dac, double freq); + + /** + * Gets the NCO frequency for a given DAC or ADC + * + * @param tile_id the tile ID of the block + * @param block_id the block ID of the block + * @param is_dac whether the block is a DAC (true) or ADC (false) + * + * @return freq of the NCO in Hz + */ + double get_nco_freq(uint32_t tile_id, uint32_t block_id, bool is_dac); + + /** + * Sets the mixer mode of the given block + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param is_dac whether the block is a DAC (true) or ADC (false) + * @param mixer_mode the mixer mode to set. + * See mixer_mode_options for valid values + * + * @return true if the operation was successful + */ + bool set_mixer_mode( + uint32_t tile_id, uint32_t block_id, bool is_dac, mixer_mode_options mixer_mode); + + /** + * Sets the Nyquist Zone of a give block + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param is_dac whether the block is a DAC (true) or ADC (false) + * @param nyquist_zone the nyquist zone to set + * See nyquist_zone_options for valid values. + * + * @return true if the operation was successful + */ + bool set_nyquist_zone(uint32_t tile_id, + uint32_t block_id, + bool is_dac, + nyquist_zone_options nyquist_zone); + + /** + * Sets the calibration mode of a given ADC + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param calibration_mode the calibration mode to set. + * See calibration_mode_options for valid values. + * See section "RF-ADC Settings" of the Xilinx + * "RF Data Converter Interface User Guide" to learn + * more about the modes. + * + * @return true if the operation was successful + */ + bool set_calibration_mode( + uint32_t tile_id, uint32_t block_id, calibration_mode_options calibration_mode); + + /** + * Enables/Disables the Inverse-Sinc filter on a DAC block. + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param enable enables the filter if true, disables if false + * + * @return true if the operation was successful + */ + bool enable_inverse_sinc_filter(uint32_t tile_id, uint32_t block_id, bool enable); + + /** + * Sets the sample rate for a given tile. + * + * @param tile_id the ID of the tile to set + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * @param sample_rate the rate in Hz to sample at + * + * @return true if the operation was successful + */ + bool set_sample_rate(uint32_t tile_id, bool is_dac, double sample_rate); + + /** + * Gets the sample rate for a given block. + * + * @param tile_id the ID of the tile to set + * @param block_id the ID of the block to set + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * + * @return sample rate of the block in Hz + */ + double get_sample_rate(uint32_t tile_id, uint32_t block_id, bool is_dac); + + /** + * Specifies the IF for the given ADC or DAC. + * Setting this will determine the Nyquist zone, mixer mode, + * Inverse Sinc filter, and mixer NCO frequency. + * + * @param tile_id the tile ID of the block to set + * @param block_id the block ID of the block to set + * @param is_dac whether the block is a DAC (true) or ADC (false) + * @param if_freq the IF frequency expected for the block. + * Frequencies are specified in Hz. + * + * @return true all resulting settings were successfully changed + */ + bool set_if(uint32_t tile_id, uint32_t block_id, bool is_dac, double if_freq); + + /** + * Sets the decimation factor for a given ADC block + * + * @param tile_id the ID of the tile to set + * @param block_id the block ID of the block to set + * @param decimation_factor the desired factor + * See interp_decim_options for valid values + * + * @return true if the operation was successful + */ + bool set_decimation_factor( + uint32_t tile_id, uint32_t block_id, interp_decim_options decimation_factor); + + /** + * Gets the decimation factor for a given ADC block + * + * @param tile_id the ID of the tile to get + * @param block_id the block ID of the block to get + * + * @return the actual decimation factor + * See interp_decim_options for valid values + */ + interp_decim_options get_decimation_factor(uint32_t tile_id, uint32_t block_id); + + /** + * Sets the interpolation factor for a given DAC block + * + * @param tile_id the ID of the tile to set + * @param block_id the block ID of the block to set + * @param interpolation_factor the desired factor + * See interp_decim_options for valid values. + * + * @return true if the operation was successful + */ + bool set_interpolation_factor( + uint32_t tile_id, uint32_t block_id, interp_decim_options interpolation_factor); + + /** + * Gets the interpolation factor for a given DAC block + * + * @param tile_id the ID of the tile to get + * @param block_id the block ID of the block to get + * + * @return the actual interpolation factor + * See interp_decim_options for valid values + */ + interp_decim_options get_interpolation_factor(uint32_t tile_id, uint32_t block_id); + + /** + * Sets the number of valid read words for a given ADC block + * + * @param tile_id the ID of the tile to set + * @param block_id the block ID of the block to set + * @param valid_read_words the number of valid read words + * + * @return true if the operation was successful + */ + bool set_data_read_rate( + uint32_t tile_id, uint32_t block_id, uint32_t valid_read_words); + + /** + * Gets the number of valid read words for a given ADC/DAC block + * + * @param tile_id the ID of the tile to get + * @param block_id the block ID of the block to get + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * + * @return the valid read words + */ + uint32_t get_data_read_rate(uint32_t tile_id, uint32_t block_id, bool is_dac); + + /** + * Sets the number of valid write words for a given DAC block + * + * @param tile_id the ID of the tile to set + * @param block_id the block ID of the block to set + * @param valid_write_words the number of valid write words + * + * @return true if the operation was successful + */ + bool set_data_write_rate( + uint32_t tile_id, uint32_t block_id, uint32_t valid_write_words); + + /** + * Gets the number of valid write words for a given ADC/DAC block + * + * @param tile_id the ID of the tile to get + * @param block_id the block ID of the block to get + * @param is_dac whether the tile is a DAC (true) or ADC (false) + + * + * @return the valid write words + */ + uint32_t get_data_write_rate(uint32_t tile_id, uint32_t block_id, bool is_dac); + + /** + * Sets the clock fabric output divider of a given tile + * + * @param tile_id the ID of the tile to set + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * @param divider the divider to set + * See fabric_clk_div_options for valid values + * + * @return true if the operation was successful + */ + bool set_fabric_clk_div( + uint32_t tile_id, bool is_dac, fabric_clk_div_options divider); + + /** + * Gets the fabric clock divider rate of a Tile + * + * @param tile_id the ID of the tile to get + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * + * @return the fabric clock divider + * See fabric_clk_div_options for valid values + */ + fabric_clk_div_options get_fabric_clk_div(uint32_t tile_id, bool is_dac); + + /** + * Sets the FIFO for an ADC/DAC + * + * @param tile_id the ID of the tile to get + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * @param enable enables (true) or disables (false) the FIFO + + * + * @return true if the operation was successful + */ + bool set_data_fifo_state(uint32_t tile_id, bool is_dac, bool enable); + + /** + * Gets the FIFO for an ADC/DAC + * + * @param tile_id the ID of the tile to get + * @param is_dac whether the tile is a DAC (true) or ADC (false) + + * + * @return true if FIFO is enabled, false if it is disabled + */ + bool get_data_fifo_state(uint32_t tile_id, bool is_dac); + + /** + * Clears the interrupts for the data FIFO (FIFOUSRDAT) + * + * @param tile_id the ID of the tile to get + * @param block_id specify ADC/DAC block + * @param is_dac whether the tile is a DAC (true) or ADC (false) + */ + void clear_data_fifo_interrupts( + const uint32_t tile_id, const uint32_t block_id, const bool is_dac); + + /** + * Perform Multi-tile Synchronization on ADC or DAC tiles + * + * @param tiles tiles vector to specify which DAC/ADC tiles to synchronize + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * + * @return true if synchronization completed successfully + */ + bool sync_tiles(const std::vector<uint32_t>& tiles, bool is_dac, uint32_t latency); + + /** + * Get post-sync latency between ADC or DAC tiles + * + * @param tile_index specify ADC or DAC target tile + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * + * @return the measured relative latency value of each tile + */ + uint32_t get_tile_latency(uint32_t tile_index, bool is_dac); + + /** + * Get post-sync offset between ADC or DAC tile and reference tile + * + * @param tile_index specify ADC or DAC target tile + * @param is_dac whether the tile is a DAC (true) or ADC (false) + * + * @return value the interface data was delayed to achieve alignment + */ + uint32_t get_tile_offset(uint32_t tile_index, bool is_dac); + + /** + * Sets whether or not the ADC calibration blocks are frozen + * + * @param tile_id specify ADC target tile + * @param block_id specify ADC block + * @param frozen specify whether or not the ADC calibration blocks should be frozen + */ + void set_cal_frozen(uint32_t tile_id, uint32_t block_id, bool frozen); + + /** + * Sets whether or not the ADC calibration blocks are frozen + * + * @param tile_id specify ADC target tile + * @param block_id specify ADC block + * + * @return true if the cal blocks are frozen, false if not + */ + bool get_cal_frozen(uint32_t tile_id, uint32_t block_id); + + void set_adc_cal_coefficients(uint32_t tile_id, uint32_t block_id, uint32_t cal_block, std::vector<uint32_t> coefs); + std::vector<uint32_t> get_adc_cal_coefficients(uint32_t tile_id, uint32_t block_id, uint32_t cal_block); + + /** + * Resets an internal mixer with known valid settings. + */ + bool reset_mixer_settings( uint32_t tile_id, uint32_t block_id, bool is_dac); + +private: + /* Indicates whether libmetal was initialized successfully and can + * be safely deinitialized. + */ + bool metal_init_complete = false; + + // Stores the current threshold clear mode according to + // [Tile ID][Block ID][Threshold ID] + threshold_clr_mode_options threshold_clr_modes[XRFDC_TILE_ID_MAX + 1] + [XRFDC_BLOCK_ID_MAX + 1] + [THRESHOLDS_PER_BLOCK]; +}; +}}; /* namespace mpm::rfdc */ + +#ifdef LIBMPM_PYTHON +void export_rfdc(py::module& top_module) +{ + using namespace mpm::rfdc; + auto m = top_module.def_submodule("rfdc"); + + py::class_<rfdc_ctrl, std::shared_ptr<rfdc_ctrl>>(m, "rfdc_ctrl") + .def(py::init()) + .def("init", &rfdc_ctrl::init) + .def("startup_tile", &rfdc_ctrl::startup_tile) + .def("shutdown_tile", &rfdc_ctrl::shutdown_tile) + .def("reset_tile", &rfdc_ctrl::reset_tile) + .def("trigger_update_event", &rfdc_ctrl::trigger_update_event) + .def("set_gain_enable", &rfdc_ctrl::set_gain_enable) + .def("set_gain", &rfdc_ctrl::set_gain) + .def("set_threshold_settings", &rfdc_ctrl::set_threshold_settings) + .def("clear_threshold_sticky", &rfdc_ctrl::clear_threshold_sticky) + .def("set_threshold_clr_mode", &rfdc_ctrl::set_threshold_clr_mode) + .def("get_threshold_clr_mode", &rfdc_ctrl::get_threshold_clr_mode) + .def("set_decoder_mode", &rfdc_ctrl::set_decoder_mode) + .def("reset_nco_phase", &rfdc_ctrl::reset_nco_phase) + .def("set_nco_event_src", &rfdc_ctrl::set_nco_event_src) + .def("set_nco_freq", &rfdc_ctrl::set_nco_freq) + .def("get_nco_freq", &rfdc_ctrl::get_nco_freq) + .def("reset_mixer_settings", &rfdc_ctrl::reset_mixer_settings) + .def("set_mixer_mode", &rfdc_ctrl::set_mixer_mode) + .def("set_nyquist_zone", &rfdc_ctrl::set_nyquist_zone) + .def("set_calibration_mode", &rfdc_ctrl::set_calibration_mode) + .def("enable_inverse_sinc_filter", &rfdc_ctrl::enable_inverse_sinc_filter) + .def("set_sample_rate", &rfdc_ctrl::set_sample_rate) + .def("get_sample_rate", &rfdc_ctrl::get_sample_rate) + .def("set_if", &rfdc_ctrl::set_if) + .def("set_decimation_factor", &rfdc_ctrl::set_decimation_factor) + .def("get_decimation_factor", &rfdc_ctrl::get_decimation_factor) + .def("set_interpolation_factor", &rfdc_ctrl::set_interpolation_factor) + .def("get_interpolation_factor", &rfdc_ctrl::get_interpolation_factor) + .def("set_data_read_rate", &rfdc_ctrl::set_data_read_rate) + .def("get_data_read_rate", &rfdc_ctrl::get_data_read_rate) + .def("set_data_write_rate", &rfdc_ctrl::set_data_write_rate) + .def("get_data_write_rate", &rfdc_ctrl::get_data_write_rate) + .def("set_fabric_clk_div", &rfdc_ctrl::set_fabric_clk_div) + .def("get_fabric_clk_div", &rfdc_ctrl::get_fabric_clk_div) + .def("set_data_fifo_state", &rfdc_ctrl::set_data_fifo_state) + .def("get_data_fifo_state", &rfdc_ctrl::get_data_fifo_state) + .def("clear_data_fifo_interrupts", &rfdc_ctrl::clear_data_fifo_interrupts) + .def("sync_tiles", &rfdc_ctrl::sync_tiles) + .def("get_tile_latency", &rfdc_ctrl::get_tile_latency) + .def("get_tile_offset", &rfdc_ctrl::get_tile_offset) + .def("set_cal_frozen", &rfdc_ctrl::set_cal_frozen) + .def("get_cal_frozen", &rfdc_ctrl::get_cal_frozen) + .def("set_adc_cal_coefficients", &rfdc_ctrl::set_adc_cal_coefficients) + .def("get_adc_cal_coefficients", &rfdc_ctrl::get_adc_cal_coefficients); + + py::enum_<mpm::rfdc::rfdc_ctrl::threshold_id_options>(m, "threshold_id_options") + .value("THRESHOLD_0", mpm::rfdc::rfdc_ctrl::THRESHOLD_0) + .value("THRESHOLD_1", mpm::rfdc::rfdc_ctrl::THRESHOLD_1) + .value("THRESHOLD_BOTH", mpm::rfdc::rfdc_ctrl::THRESHOLD_BOTH); + + py::enum_<mpm::rfdc::rfdc_ctrl::threshold_mode_options>(m, "threshold_mode_options") + .value("TRSHD_OFF", mpm::rfdc::rfdc_ctrl::TRSHD_OFF) + .value("TRSHD_STICKY_OVER", mpm::rfdc::rfdc_ctrl::TRSHD_STICKY_OVER) + .value("TRSHD_STICKY_UNDER", mpm::rfdc::rfdc_ctrl::TRSHD_STICKY_UNDER) + .value("TRSHD_HYSTERESIS", mpm::rfdc::rfdc_ctrl::TRSHD_HYSTERESIS); + + py::enum_<mpm::rfdc::rfdc_ctrl::threshold_clr_mode_options>( + m, "threshold_clr_mode_options") + .value("THRESHOLD_CLRMD_MANUAL", mpm::rfdc::rfdc_ctrl::THRESHOLD_CLRMD_MANUAL) + .value("THRESHOLD_CLRMD_AUTO", mpm::rfdc::rfdc_ctrl::THRESHOLD_CLRMD_AUTO) + .value("THRESHOLD_CLRMD_UNKNOWN", mpm::rfdc::rfdc_ctrl::THRESHOLD_CLRMD_UNKNOWN); + + py::enum_<mpm::rfdc::rfdc_ctrl::decoder_mode_options>(m, "decoder_mode_options") + .value("DECODER_MAX_SNR_MODE", mpm::rfdc::rfdc_ctrl::DECODER_MAX_SNR_MODE) + .value("DECODER_MAX_LINEARITY_MODE", + mpm::rfdc::rfdc_ctrl::DECODER_MAX_LINEARITY_MODE); + + py::enum_<mpm::rfdc::rfdc_ctrl::nyquist_zone_options>(m, "nyquist_zone_options") + .value("ODD_NYQUIST_ZONE", mpm::rfdc::rfdc_ctrl::ODD_NYQUIST_ZONE) + .value("EVEN_NYQUIST_ZONE", mpm::rfdc::rfdc_ctrl::EVEN_NYQUIST_ZONE); + + py::enum_<mpm::rfdc::rfdc_ctrl::mixer_mode_options>(m, "mixer_mode_options") + .value("MIXER_MODE_OFF", mpm::rfdc::rfdc_ctrl::MIXER_MODE_OFF) + .value("MIXER_MODE_C2C", mpm::rfdc::rfdc_ctrl::MIXER_MODE_C2C) + .value("MIXER_MODE_C2R", mpm::rfdc::rfdc_ctrl::MIXER_MODE_C2R) + .value("MIXER_MODE_R2C", mpm::rfdc::rfdc_ctrl::MIXER_MODE_R2C) + .value("MIXER_MODE_R2R", mpm::rfdc::rfdc_ctrl::MIXER_MODE_R2R); + + py::enum_<mpm::rfdc::rfdc_ctrl::calibration_mode_options>( + m, "calibration_mode_options") + .value("CALIB_MODE1", mpm::rfdc::rfdc_ctrl::CALIB_MODE1) + .value("CALIB_MODE2", mpm::rfdc::rfdc_ctrl::CALIB_MODE2); + + py::enum_<mpm::rfdc::rfdc_ctrl::event_type_options>(m, "event_type_options") + .value("MIXER_EVENT", mpm::rfdc::rfdc_ctrl::MIXER_EVENT) + .value("CRSE_DLY_EVENT", mpm::rfdc::rfdc_ctrl::CRSE_DLY_EVENT) + .value("QMC_EVENT", mpm::rfdc::rfdc_ctrl::QMC_EVENT); + + py::enum_<mpm::rfdc::rfdc_ctrl::interp_decim_options>(m, "interp_decim_options") + .value("INTERP_DECIM_OFF", mpm::rfdc::rfdc_ctrl::INTERP_DECIM_OFF) + .value("INTERP_DECIM_1X", mpm::rfdc::rfdc_ctrl::INTERP_DECIM_1X) + .value("INTERP_DECIM_2X", mpm::rfdc::rfdc_ctrl::INTERP_DECIM_2X) + .value("INTERP_DECIM_4X", mpm::rfdc::rfdc_ctrl::INTERP_DECIM_4X) + .value("INTERP_DECIM_8X", mpm::rfdc::rfdc_ctrl::INTERP_DECIM_8X); + + py::enum_<mpm::rfdc::rfdc_ctrl::fabric_clk_div_options>(m, "fabric_clk_div_options") + .value("DIV_1", mpm::rfdc::rfdc_ctrl::DIV_1) + .value("DIV_2", mpm::rfdc::rfdc_ctrl::DIV_2) + .value("DIV_4", mpm::rfdc::rfdc_ctrl::DIV_4) + .value("DIV_8", mpm::rfdc::rfdc_ctrl::DIV_8) + .value("DIV_16", mpm::rfdc::rfdc_ctrl::DIV_16); +} +#endif diff --git a/mpm/include/mpm/rfdc/rfdc_throw.h b/mpm/include/mpm/rfdc/rfdc_throw.h new file mode 100755 index 000000000..20a592696 --- /dev/null +++ b/mpm/include/mpm/rfdc/rfdc_throw.h @@ -0,0 +1,10 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +/** + * A function to throw MPM exceptions from within the Xilinx RFdc API + */ +void rfdc_throw(const char* msg); diff --git a/mpm/include/mpm/rfdc/xrfdc.h b/mpm/include/mpm/rfdc/xrfdc.h new file mode 100644 index 000000000..ad2c06fa8 --- /dev/null +++ b/mpm/include/mpm/rfdc/xrfdc.h @@ -0,0 +1,2092 @@ +/****************************************************************************** +* +* Copyright (C) 2017-2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc.h +* @addtogroup rfdc_v6_0 +* @{ +* @details +* +* The Xilinx� LogiCORE IP Zynq UltraScale+ RFSoC RF Data Converter IP core +* provides a configurable wrapper to allow the RF DAC and RF ADC blocks to be +* used in IP Integrator designs. Multiple tiles are available on each RFSoC +* and each tile can have a number of data converters (analog-to-digital (ADC) +* and digital-to-analog (DAC)). The RF ADCs can sample input frequencies up +* to 4 GHz at 4 GSPS with excellent noise spectral density. The RF DACs +* generate output carrier frequencies up to 4 GHz using the 2nd Nyquist zone +* with excellent noise spectral density at an update rate of 6.4 GSPS. +* The RF data converters also include power efficient digital down-converters +* (DDCs) and digital up-converters (DUCs) that include programmable interpolation +* and decimation, NCO and complex mixer. The DDCs and DUCs can also support +* dual-band operation. +* A maximum of 4 tiles are available on for DAC and ADC operations each. Each +* tile can have a maximum of 4 blocks/slices. +* This driver provides APIs to configure various functionalities. Similarly +* the driver provides APIs to read back configurations. +* Some of the features that the driver supports are: +* 1) Setting up and reading back fine mixer settings +* 2) Setting up and reading back coarse mixer settings +* 3) Reading back interpolation or decimation factors +* 4) Setting up and reading back QMC settings which include gain, phase etc +* 5) Setting up and reading back decoder mode settings +* 6) Setting up and reading back coarse delay settings +* All the APIs implemented in the driver provide appropriate range checks. +* An API has been provided for debug purpose which will dump all registers +* for a requested tile. +* Inline functions have also been provided to read back the parameters +* initially configured through the GUI. +* +* There are plans to add more features, e.g. Support for multi band, PLL +* configurations etc. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.0 sk 05/16/17 Initial release +* 2.0 sk 08/09/17 Fixed coarse Mixer configuration settings +* CR# 977266, 977872. +* Return error for Slice Event on 4G ADC Block. +* Corrected Interrupt Macro names and values. +* 08/16/17 Add support for SYSREF and PL event sources. +* 08/18/17 Add API to enable and disable FIFO. +* 08/23/17 Add API to configure Nyquist zone. +* 08/30/17 Add additional info to BlockStatus. +* 08/30/17 Add support for Coarse Mixer BYPASS mode. +* 08/31/17 Removed Tile Reset Assert and Deassert. +* 09/07/17 Add support for negative NCO freq. +* 09/15/17 Fixed NCO freq precision issue. +* 09/15/17 Fixed Immediate Event source issue and also +* updated the Immediate Macro value to 0. +* 2.1 sk 09/15/17 Remove Libmetal library dependency for MB. +* 09/18/17 Add API to clear the interrupts. +* sk 09/21/17 Add __BAREMETAL__ compiler flag option +* for Baremetal. +* sk 09/21/17 Add support for Over voltage and Over +* Range interrupts. +* sk 09/22/17 Add s64 typedef for Linux. +* sk 09/24/17 Fixed Get_Tile/BlockBaseAddr always +* giving ADC related address. +* sk 09/25/17 Modified XRFdc_GetBlockStatus API to give +* correct information and also updates the +* description for Vector Param in intr handler +* Add API to get Output current and removed +* GetTermVoltage and GetOutputCurr inline functions. +* 2.2 sk 10/05/17 Fixed XRFdc_GetNoOfADCBlocks API for 4GSPS. +* Enable the decoder clock based on decoder mode. +* Add API to get the current FIFO status. +* Updated XRFdc_DumpRegs API for better readability +* of output register dump. +* Add support for 4GSPS CoarseMixer frequency. +* 10/11/17 Modify float types to double to increase precision. +* 10/12/17 Update BlockStatus API to give current status. +* In BYPASS mode, input datatype can be Real or IQ +* hence checked both while reading the mixer mode. +* 10/17/17 Fixed Set Threshold API Issue. +* 2.2 sk 10/18/17 Add support for FIFO and DATA overflow interrupt +* 2.3 sk 11/06/17 Fixed PhaseOffset truncation issue. +* Provide user configurability for FineMixerScale. +* 11/08/17 Return error for DAC R2C mode and ADC C2R mode. +* 11/10/17 Corrected FIFO and DATA Interrupt masks. +* 11/20/17 Fixed StartUp, Shutdown and Reset API for Tile_Id -1. +* 11/20/17 Remove unwanted ADC block checks in 4GSPS mode. +* 3.0 sk 12/11/17 Added DDC and DUC support. +* 12/13/17 Add CoarseMixMode field in Mixer_Settings structure. +* 12/15/17 Add support to switch calibration modes. +* 12/15/17 Add support for mixer frequencies > Fs/2 and < -Fs/2. +* sg 13/01/18 Added PLL and external clock switch support +* Added API to get PLL lock status. +* Added API to get clock source. +* sk 01/18/18 Add API to get driver version. +* 3.1 jm 01/24/18 Add Multi-tile sync support. +* sk 01/25/18 Updated Set and Get Interpolation/Decimation factor +* API's to consider the actual factor value. +* 3.2 sk 02/02/18 Add API's to configure inverse-sinc. +* sk 02/27/18 Add API's to configure Multiband. +* sk 03/09/18 Update PLL structure in XRFdc_DynamicPLLConfig API. +* sk 03/09/18 Update ADC and DAC datatypes in Mixer API and use +* input datatype for ADC in threshold and QMC APIs. +* sk 03/09/18 Removed FIFO disable check in DDC and DUC APIs. +* sk 03/09/18 Add support for Marker event source for DAC block. +* jm 03/12/18 Fixed DAC latency calculation in MTS. +* jm 03/12/18 Added support for reloading DTC scans. +* jm 03/12/18 Add option to configure sysref capture after MTS. +* sk 03/22/18 Updated PLL settings based on latest IP values. +* 4.0 sk 04/09/18 Added API to enable/disable the sysref. +* sk 04/09/18 Updated max VCO to 13108MHz to support max DAC +* sample rate of 6.554MHz. +* rk 04/17/18 Adjust calculated latency by sysref period, where doing +* so results in closer alignment to the target latency. +* sk 04/17/18 Corrected Set/Get MixerSettings API description for +* FineMixerScale parameter. +* sk 04/19/18 Enable VCO Auto selection while configuring the clock. +* sk 04/24/18 Add API to get PLL Configurations. +* sk 04/24/18 Add API to get the Link Coupling mode. +* sk 04/28/18 Implement timeouts for PLL Lock, Startup and shutdown. +* sk 05/30/18 Removed CalibrationMode check for DAC. +* sk 06/05/18 Updated minimum Ref clock value to 102.40625MHz. +* 5.0 sk 06/25/18 Update DAC min sampling rate to 500MHz and also update +* VCO Range, PLL_DIVIDER and PLL_FPDIV ranges. +* Update PLL structure with calculated sampling rate. +* sk 06/25/18 Add XRFdc_GetFabClkOutDiv() API to read fabric clk div. +* Add Inline APIs XRFdc_CheckBlockEnabled(), +* XRFdc_CheckTileEnabled(). +* sk 07/06/18 Add support to dump HSCOM regs in XRFdc_DumpRegs() API +* sk 07/12/18 Fixed Multiband crossbar settings in C2C mode. +* sk 07/19/18 Add MixerType member to MixerSettings structure and +* Update Mixer Settings APIs to consider the MixerType +* variable. +* sk 07/19/18 Add XRFdc_GetMultibandConfig() API to read Multiband +* configuration. +* sk 07/20/18 Update the APIs to check the corresponding section +* (Digital/Analog)enable/disable. +* sk 07/26/18 Fixed Doxygen, coverity warnings. +* sk 08/03/18 Fixed MISRAC warnings. +* sk 08/24/18 Move mixer related APIs to xrfdc_mixer.c file. +* Define asserts for Linux, Re-arranged XRFdc_RestartIPSM, +* XRFdc_CfgInitialize() and XRFdc_MultiBand() APIs. +* Reorganize the code to improve readability and +* optimization. +* mus 08/17/18 Removed structure paddings from XRFdc_Config structure. +* It has been done to have 1:1 mapping between +* XRFdc_Config structure and device tree property +* "param-list", over linux platform. +* sk 09/24/18 Update powerup-state value based on PLL mode in +* XRFdc_DynamicPLLConfig() API. +* sk 10/10/18 Check for DigitalPath enable in XRFdc_GetNyquistZone() +* and XRFdc_GetCalibrationMode() APIs for Multiband. +* sk 10/13/18 Add support to read the REFCLKDIV param from design. +* Update XRFdc_SetPLLConfig() API to support range of +* REF_CLK_DIV values(1 to 4). +* Add XRFDC_MIXER_MODE_R2R option to support BYPASS mode +* for Real input. +* 5.1 cog 01/29/19 Replace structure reference ADC checks with +* function. +* cog 01/29/19 Added XRFdc_SetDither() and XRFdc_GetDither() APIs. +* cog 01/29/19 Rename DataType for mixer input to MixerInputDataType +* for readability. +* cog 01/29/19 Refactoring of interpolation and decimation APIs and +* changed fabric rate for decimation X8 for non-high speed ADCs. +* cog 01/29/19 New inline functions to determine max & min sampling rates. +* 6.0 cog 02/17/19 Added Inverse-Sinc Second Nyquist Zone Support +* cog 02/17/19 Added new clock Distribution functionality. +* cog 02/17/19 Refactored to improve delay balancing in clock +* distribution. +* cog 02/17/19 Added delay calculation & metal log messages. +* cog 02/17/19 Added Intratile clock settings. +* cog 02/17/19 XRFdc_GetPLLConfig() now uses register values to get the +* PLL configuration for new IPs and is no longer static. +* cog 02/17/19 Refactoring of interpolation and decimation APIs and +* changed fabric rate for decimation X8 for non-high speed ADCs. +* cog 02/17/19 Added XRFdc_SetIMRPassMode() and XRFdc_SetIMRPassMode() APIs +* cog 02/17/19 Added XRFdc_SetDACMode() and XRFdc_GetDACMode() APIs +* cog 02/17/19 Added XRFdc_SetSignalDetector() and XRFdc_GetSignalDetector() APIs +* cog 02/17/19 Added XRFdc_DisableCoefficientsOverride(), XRFdc_SetCalCoefficients +* and XRFdc_GetCalCoefficients APIs. +* cog 02/19/19 New definitions for clock detection. +* 6.0 cog 02/20/19 Added handling for new ADC common mode over/under +* voltage interrupts. +* cog 02/20/19 XRFdc_GetIntrStatus now populates a pointer with the +* status and returns an error code. +* cog 02/20/19 XRFdc_IntrClr, XRFdc_IntrDisable and XRFdc_IntrEnable +* now return error codes. +* cog 02/21/19 Added XRFdc_SetCalFreeze() and XRFdc_GetCalFreeze() APIs +* cog 04/15/19 Rename XRFdc_SetDACMode() and XRFdc_GetDACMode() APIs to +* XRFdc_SetDataPathMode() and XRFdc_GetDataPathMode() respectively. +* +* </pre> +* +******************************************************************************/ + + +#ifndef RFDC_H_ +#define RFDC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************** Include Files *********************************/ + +#include "rfdc_throw.h" +#include <stdlib.h> +#include <stdint.h> + +#ifdef __BAREMETAL__ +#include "xil_assert.h" +#include "xdebug.h" +#include "sleep.h" +#endif +#include <metal/sys.h> +#include <metal/device.h> +#include <metal/irq.h> +#include <metal/atomic.h> +#include <metal/io.h> +#include <metal/sleep.h> +#include "metal/alloc.h" +#include "xrfdc_hw.h" + +/**************************** Type Definitions *******************************/ +#define XRFdc_IsADC4GSPS(InstPtr) XRFdc_IsHighSpeedADC(InstPtr, 0) + +#ifndef __BAREMETAL__ +typedef __u32 u32; +typedef __u16 u16; +typedef __u8 u8; +typedef __s32 s32; +typedef __u64 u64; +typedef __s64 s64; +typedef __s8 s8; +#endif + +/** +* The handler data type allows the user to define a callback function to +* respond to interrupt events in the system. This function is executed +* in interrupt context, so amount of processing should be minimized. +* +* @param CallBackRef is the callback reference passed in by the upper +* layer when setting the callback functions, and passed back to +* the upper layer when the callback is invoked. Its type is +* not important to the driver, so it is a void pointer. +* @param Type indicates ADC/DAC. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number (0-3). +* @param StatusEvent indicates one or more interrupt occurred. +*/ +typedef void (*XRFdc_StatusHandler) (void *CallBackRef, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 StatusEvent); +#ifndef __BAREMETAL__ +#pragma pack(1) +#endif +/** + * PLL settings. + */ +typedef struct { + u32 Enabled; /* PLL Enables status (not a setter) */ + double RefClkFreq; + double SampleRate; + u32 RefClkDivider; + u32 FeedbackDivider; + u32 OutputDivider; + u32 FractionalMode; /* Fractional mode is currently not supported */ + u64 FractionalData; /* Fractional data is currently not supported */ + u32 FractWidth; /* Fractional width is currently not supported */ +} XRFdc_PLL_Settings; +/** +* ClkIntraTile Settings. +*/ +typedef struct { + u8 SourceTile; + u8 PLLEnable; + XRFdc_PLL_Settings PLLSettings; + u8 DivisionFactor; + u8 Delay; + u8 DistributedClock; +} XRFdc_Tile_Clock_Settings; +/** +* Clk Distribution. +*/ +typedef struct { + u8 Enabled; + u8 DistributionSource; + u8 UpperBound; + u8 LowerBound; + u8 MaxDelay; + u8 MinDelay; + u8 IsDelayBalanced; +} XRFdc_Distribution; +/** +* Clk Distribution Settings. +*/ +typedef struct { + XRFdc_Tile_Clock_Settings DAC[4]; + XRFdc_Tile_Clock_Settings ADC[4]; + XRFdc_Distribution DistributionStatus[8]; +} XRFdc_Distribution_Settings; +#ifndef __BAREMETAL__ +#pragma pack() +#endif + +/** + * ADC Signal Detect Settings. + */ +typedef struct { + u8 Mode; + u8 TimeConstant; + u8 Flush; + u8 EnableIntegrator; + u16 HighThreshold; + u16 LowThreshold; + u8 HysteresisEnable; +}XRFdc_Signal_Detector_Settings; +/** + * QMC settings. + */ +typedef struct { + u32 EnablePhase; + u32 EnableGain; + double GainCorrectionFactor; + double PhaseCorrectionFactor; + s32 OffsetCorrectionFactor; + u32 EventSource; +} XRFdc_QMC_Settings; + +/** + * Coarse delay settings. + */ +typedef struct { + u32 CoarseDelay; + u32 EventSource; +} XRFdc_CoarseDelay_Settings; + +/** + * Mixer settings. + */ +typedef struct { + double Freq; + double PhaseOffset; + u32 EventSource; + u32 CoarseMixFreq; + u32 MixerMode; + u8 FineMixerScale; /* NCO output scale, valid values 0,1 and 2 */ + u8 MixerType; +} XRFdc_Mixer_Settings; + +/** + * ADC block Threshold settings. + */ +typedef struct { + u32 UpdateThreshold; /* Selects which threshold to update */ + u32 ThresholdMode[2]; /* Entry 0 for Threshold0 and 1 for Threshold1 */ + u32 ThresholdAvgVal[2]; /* Entry 0 for Threshold0 and 1 for Threshold1 */ + u32 ThresholdUnderVal[2]; /* Entry 0 for Threshold0 and 1 for Threshold1 */ + u32 ThresholdOverVal[2]; /* Entry 0 is for Threshold0 and 1 for Threshold1 */ +} XRFdc_Threshold_Settings; + +/** + * RFSoC Calibration coefficients generic struct + */ +typedef struct { + u32 Coeff0; + u32 Coeff1; + u32 Coeff2; + u32 Coeff3; + u32 Coeff4; + u32 Coeff5; + u32 Coeff6; + u32 Coeff7; +} XRFdc_Calibration_Coefficients; + +/** + * RFSoC Calibration freeze settings struct + */ +typedef struct { + u32 CalFrozen; /*Status indicates calibration freeze state*/ + u32 DisableFreezePin; /*Disable the calibration freeze pin*/ + u32 FreezeCalibration; /*Setter for freezing*/ +} XRFdc_Cal_Freeze_Settings; + +/** + * RFSoC Tile status. + */ +typedef struct { + u32 IsEnabled; /* 1, if tile is enabled, 0 otherwise */ + u32 TileState; /* Indicates Tile Current State */ + u8 BlockStatusMask; /* Bit mask for block status, 1 indicates block enable */ + u32 PowerUpState; + u32 PLLState; +} XRFdc_TileStatus; + +/** + * RFSoC Data converter IP status. + */ +typedef struct { + XRFdc_TileStatus DACTileStatus[4]; + XRFdc_TileStatus ADCTileStatus[4]; + u32 State; +} XRFdc_IPStatus; + +/** + * status of DAC or ADC blocks in the RFSoC Data converter. + */ +typedef struct { + double SamplingFreq; + u32 AnalogDataPathStatus; + u32 DigitalDataPathStatus; + u8 DataPathClocksStatus; /* Indicates all required datapath + clocks are enabled or not, 1 if all clocks enabled, 0 otherwise */ + u8 IsFIFOFlagsEnabled; /* Indicates FIFO flags enabled or not, + 1 if all flags enabled, 0 otherwise */ + u8 IsFIFOFlagsAsserted; /* Indicates FIFO flags asserted or not, + 1 if all flags asserted, 0 otherwise */ +} XRFdc_BlockStatus; + +#ifndef __BAREMETAL__ +#pragma pack(1) +#endif +/** + * DAC block Analog DataPath Config settings. + */ +typedef struct { + u32 BlockAvailable; + u32 InvSyncEnable; + u32 MixMode; + u32 DecoderMode; +} XRFdc_DACBlock_AnalogDataPath_Config; + +/** + * DAC block Digital DataPath Config settings. + */ +typedef struct { + u32 MixerInputDataType; + u32 DataWidth; + u32 InterpolationMode; + u32 FifoEnable; + u32 AdderEnable; + u32 MixerType; +} XRFdc_DACBlock_DigitalDataPath_Config; + +/** + * ADC block Analog DataPath Config settings. + */ +typedef struct { + u32 BlockAvailable; + u32 MixMode; +} XRFdc_ADCBlock_AnalogDataPath_Config; + +/** + * DAC block Digital DataPath Config settings. + */ +typedef struct { + u32 MixerInputDataType; + u32 DataWidth; + u32 DecimationMode; + u32 FifoEnable; + u32 MixerType; +} XRFdc_ADCBlock_DigitalDataPath_Config; + +/** + * DAC Tile Config structure. + */ +typedef struct { + u32 Enable; + u32 PLLEnable; + double SamplingRate; + double RefClkFreq; + double FabClkFreq; + u32 FeedbackDiv; + u32 OutputDiv; + u32 RefClkDiv; + u32 MultibandConfig; + double MaxSampleRate; + u32 NumSlices; + XRFdc_DACBlock_AnalogDataPath_Config DACBlock_Analog_Config[4]; + XRFdc_DACBlock_DigitalDataPath_Config DACBlock_Digital_Config[4]; +} XRFdc_DACTile_Config; + +/** + * ADC Tile Config Structure. + */ +typedef struct { + u32 Enable; /* Tile Enable status */ + u32 PLLEnable; /* PLL enable Status */ + double SamplingRate; + double RefClkFreq; + double FabClkFreq; + u32 FeedbackDiv; + u32 OutputDiv; + u32 RefClkDiv; + u32 MultibandConfig; + double MaxSampleRate; + u32 NumSlices; + XRFdc_ADCBlock_AnalogDataPath_Config ADCBlock_Analog_Config[4]; + XRFdc_ADCBlock_DigitalDataPath_Config ADCBlock_Digital_Config[4]; +} XRFdc_ADCTile_Config; + +/** + * RFdc Config Structure. + */ +typedef struct { + u32 DeviceId; + metal_phys_addr_t BaseAddr; + u32 ADCType; /* ADC Type 4GSPS or 2GSPS*/ + u32 MasterADCTile; /* ADC master Tile */ + u32 MasterDACTile; /* DAC Master Tile */ + u32 ADCSysRefSource; + u32 DACSysRefSource; + u32 IPType; + XRFdc_DACTile_Config DACTile_Config[4]; + XRFdc_ADCTile_Config ADCTile_Config[4]; +} XRFdc_Config; +#ifndef __BAREMETAL__ +#pragma pack() +#endif +/** + * DAC Block Analog DataPath Structure. + */ +typedef struct { + u32 Enabled; /* DAC Analog Data Path Enable */ + u32 MixedMode; + double TerminationVoltage; + double OutputCurrent; + u32 InverseSincFilterEnable; + u32 DecoderMode; + void *FuncHandler; + u32 NyquistZone; + u8 AnalogPathEnabled; + u8 AnalogPathAvailable; + XRFdc_QMC_Settings QMC_Settings; + XRFdc_CoarseDelay_Settings CoarseDelay_Settings; +} XRFdc_DACBlock_AnalogDataPath; + +/** + * DAC Block Digital DataPath Structure. + */ +typedef struct { + u32 MixerInputDataType; + u32 DataWidth; + int ConnectedIData; + int ConnectedQData; + u32 InterpolationFactor; + u8 DigitalPathEnabled; + u8 DigitalPathAvailable; + XRFdc_Mixer_Settings Mixer_Settings; +} XRFdc_DACBlock_DigitalDataPath; + +/** + * ADC Block Analog DataPath Structure. + */ +typedef struct { + u32 Enabled; /* ADC Analog Data Path Enable */ + XRFdc_QMC_Settings QMC_Settings; + XRFdc_CoarseDelay_Settings CoarseDelay_Settings; + XRFdc_Threshold_Settings Threshold_Settings; + u32 NyquistZone; + u8 CalibrationMode; + u8 AnalogPathEnabled; + u8 AnalogPathAvailable; +} XRFdc_ADCBlock_AnalogDataPath; + +/** + * ADC Block Digital DataPath Structure. + */ +typedef struct { + u32 MixerInputDataType; + u32 DataWidth; + u32 DecimationFactor; + int ConnectedIData; + int ConnectedQData; + u8 DigitalPathEnabled; + u8 DigitalPathAvailable; + XRFdc_Mixer_Settings Mixer_Settings; +} XRFdc_ADCBlock_DigitalDataPath; + +/** + * DAC Tile Structure. + */ +typedef struct { + u32 TileBaseAddr; /* Tile BaseAddress*/ + u32 NumOfDACBlocks; /* Number of DAC block enabled */ + XRFdc_PLL_Settings PLL_Settings; + u8 MultibandConfig; + XRFdc_DACBlock_AnalogDataPath DACBlock_Analog_Datapath[4]; + XRFdc_DACBlock_DigitalDataPath DACBlock_Digital_Datapath[4]; +} XRFdc_DAC_Tile; + +/** + * ADC Tile Structure. + */ +typedef struct { + u32 TileBaseAddr; + u32 NumOfADCBlocks; /* Number of ADC block enabled */ + XRFdc_PLL_Settings PLL_Settings; + u8 MultibandConfig; + XRFdc_ADCBlock_AnalogDataPath ADCBlock_Analog_Datapath[4]; + XRFdc_ADCBlock_DigitalDataPath ADCBlock_Digital_Datapath[4]; +} XRFdc_ADC_Tile; + +/** + * RFdc Structure. + */ +typedef struct { + XRFdc_Config RFdc_Config; /* Config Structure */ + u32 IsReady; + u32 ADC4GSPS; + metal_phys_addr_t BaseAddr; /* BaseAddress */ + struct metal_io_region *io; /* Libmetal IO structure */ + struct metal_device *device; /* Libmetal device structure */ + XRFdc_DAC_Tile DAC_Tile[4]; + XRFdc_ADC_Tile ADC_Tile[4]; + XRFdc_StatusHandler StatusHandler; /* Event handler function */ + void *CallBackRef; /* Callback reference for event handler */ + u8 UpdateMixerScale; /* Set to 1, if user overwrite mixer scale */ +} XRFdc; + +/***************** Macros (Inline Functions) Definitions *********************/ + +# ifndef __BAREMETAL__ +# define Xil_AssertNonvoid(Expression) \ + { \ + if (!(Expression)) { \ + rfdc_throw(#Expression); \ + } \ + } +# define Xil_AssertVoid(Expression) \ + { \ + if (!(Expression)) { \ + rfdc_throw(#Expression); \ + } \ + } +# define Xil_AssertVoidAlways() \ + { \ + rfdc_throw("Assert false"); \ + } +# endif + +#define MAX(x,y) (x>y)?x:y +#define MIN(x,y) (x<y)?x:y +#define XRFDC_SUCCESS 0U +#define XRFDC_FAILURE 1U +#define XRFDC_COMPONENT_IS_READY 0x11111111U +#define XRFDC_NUM_SLICES_HSADC 2 +#define XRFDC_NUM_SLICES_LSADC 4 +#ifndef __BAREMETAL__ +#define XRFDC_PLATFORM_DEVICE_DIR "/sys/bus/platform/devices/" +#define XRFDC_BUS_NAME "platform" +#define XRFDC_SIGNATURE "usp_rf_data_converter" /* String in RFDC node name */ +#define XRFDC_CONFIG_DATA_PROPERTY "param-list" /* device tree property */ +#define XRFDC_COMPATIBLE_PROPERTY "compatible" /* device tree property */ +#define XRFDC_NUM_INSTANCES_PROPERTY "num-insts" /* device tree property */ +#define XRFDC_COMPATIBLE_STRING "xlnx,usp-rf-data-converter-" +#define XRFDC_DEVICE_ID_SIZE 4U +#define XRFDC_NUM_INST_SIZE 4U +#define XRFDC_CONFIG_DATA_SIZE sizeof(XRFdc_Config) +#endif +#define XRFDC_REGION_SIZE 0x40000U +#define XRFDC_DRP_BASE(type, tile) ((type) == XRFDC_ADC_TILE ? \ + XRFDC_ADC_TILE_DRP_ADDR(tile) : XRFDC_DAC_TILE_DRP_ADDR(tile)) + +#define XRFDC_CTRL_STS_BASE(Type, Tile) ((Type) == XRFDC_ADC_TILE ? \ + XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile) : \ + XRFDC_DAC_TILE_CTRL_STATS_ADDR(Tile)) + +#define XRFDC_BLOCK_BASE(Type, Tile, Block) ((Type) == XRFDC_ADC_TILE ? \ + (XRFDC_ADC_TILE_DRP_ADDR(Tile) + XRFDC_BLOCK_ADDR_OFFSET(Block)) : \ + (XRFDC_DAC_TILE_DRP_ADDR(Tile) + XRFDC_BLOCK_ADDR_OFFSET(Block))) + +#define XRFDC_ADC_TILE 0U +#define XRFDC_DAC_TILE 1U +#define XRFDC_TILE_ID_MAX 0x3U +#define XRFDC_BLOCK_ID_MAX 0x3U +#define XRFDC_EVNT_SRC_IMMEDIATE 0x00000000U +#define XRFDC_EVNT_SRC_SLICE 0x00000001U +#define XRFDC_EVNT_SRC_TILE 0x00000002U +#define XRFDC_EVNT_SRC_SYSREF 0x00000003U +#define XRFDC_EVNT_SRC_MARKER 0x00000004U +#define XRFDC_EVNT_SRC_PL 0x00000005U +#define XRFDC_EVENT_MIXER 0x00000001U +#define XRFDC_EVENT_CRSE_DLY 0x00000002U +#define XRFDC_EVENT_QMC 0x00000004U +#define XRFDC_SELECT_ALL_TILES -1 +#define XRFDC_ADC_4GSPS 1U + +#define XRFDC_CRSE_DLY_MAX 0x7U +#define XRFDC_CRSE_DLY_MAX_EXT 0x28U +#define XRFDC_NCO_FREQ_MULTIPLIER ((0x1LLU << 48U) - 2) /* 2^48 -2 */ +#define XRFDC_NCO_FREQ_MIN_MULTIPLIER (0x1LLU << 48U) /* 2^48 */ +#define XRFDC_NCO_PHASE_MULTIPLIER (1U << 17U) /* 2^17 */ +#define XRFDC_QMC_PHASE_MULT (1U << 11U) /* 2^11 */ +#define XRFDC_QMC_GAIN_MULT (1U << 14U) /* 2^14 */ + +#define XRFDC_DATA_TYPE_IQ 0x00000001U +#define XRFDC_DATA_TYPE_REAL 0x00000000U + +#define XRFDC_TRSHD_OFF 0x0U +#define XRFDC_TRSHD_STICKY_OVER 0x00000001U +#define XRFDC_TRSHD_STICKY_UNDER 0x00000002U +#define XRFDC_TRSHD_HYSTERISIS 0x00000003U + +/* Mixer modes */ +#define XRFDC_MIXER_MODE_OFF 0x0U +#define XRFDC_MIXER_MODE_C2C 0x1U +#define XRFDC_MIXER_MODE_C2R 0x2U +#define XRFDC_MIXER_MODE_R2C 0x3U +#define XRFDC_MIXER_MODE_R2R 0x4U + +#define XRFDC_I_IQ_COS_MINSIN 0x00000C00U +#define XRFDC_Q_IQ_SIN_COS 0x00001000U +#define XRFDC_EN_I_IQ 0x00000001U +#define XRFDC_EN_Q_IQ 0x00000004U + +#define XRFDC_MIXER_TYPE_COARSE 0x1U +#define XRFDC_MIXER_TYPE_FINE 0x2U + +#define XRFDC_MIXER_TYPE_OFF 0x3U + +#define XRFDC_COARSE_MIX_OFF 0x0U +#define XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO 0x2U +#define XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR 0x4U +#define XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR 0x8U +#define XRFDC_COARSE_MIX_BYPASS 0x10U + +#define XRFDC_COARSE_MIX_MODE_C2C_C2R 0x1U +#define XRFDC_COARSE_MIX_MODE_R2C 0x2U + +#define XRFDC_CRSE_MIX_OFF 0x924U +#define XRFDC_CRSE_MIX_BYPASS 0x0U +#define XRFDC_CRSE_4GSPS_ODD_FSBYTWO 0x492U +#define XRFDC_CRSE_MIX_I_ODD_FSBYFOUR 0x2CBU +#define XRFDC_CRSE_MIX_Q_ODD_FSBYFOUR 0x659U +#define XRFDC_CRSE_MIX_I_Q_FSBYTWO 0x410U +#define XRFDC_CRSE_MIX_I_FSBYFOUR 0x298U +#define XRFDC_CRSE_MIX_Q_FSBYFOUR 0x688U +#define XRFDC_CRSE_MIX_I_MINFSBYFOUR 0x688U +#define XRFDC_CRSE_MIX_Q_MINFSBYFOUR 0x298U +#define XRFDC_CRSE_MIX_R_I_FSBYFOUR 0x8A0U +#define XRFDC_CRSE_MIX_R_Q_FSBYFOUR 0x70CU +#define XRFDC_CRSE_MIX_R_I_MINFSBYFOUR 0x8A0U +#define XRFDC_CRSE_MIX_R_Q_MINFSBYFOUR 0x31CU + +#define XRFDC_MIXER_SCALE_AUTO 0x0U +#define XRFDC_MIXER_SCALE_1P0 0x1U +#define XRFDC_MIXER_SCALE_0P7 0x2U + +#define XRFDC_MIXER_PHASE_OFFSET_UP_LIMIT 180 +#define XRFDC_MIXER_PHASE_OFFSET_LOW_LIMIT (-180) +#define XRFDC_UPDATE_THRESHOLD_0 0x1U +#define XRFDC_UPDATE_THRESHOLD_1 0x2U +#define XRFDC_UPDATE_THRESHOLD_BOTH 0x4U +#define XRFDC_THRESHOLD_CLRMD_MANUAL_CLR 0x1U +#define XRFDC_THRESHOLD_CLRMD_AUTO_CLR 0x2U +#define XRFDC_DECODER_MAX_SNR_MODE 0x1U +#define XRFDC_DECODER_MAX_LINEARITY_MODE 0x2U +#define XRFDC_OUTPUT_CURRENT_32MA 32U +#define XRFDC_OUTPUT_CURRENT_20MA 20U + +#define XRFDC_ADC_MIXER_MODE_IQ 0x1U +#define XRFDC_DAC_MIXER_MODE_REAL 0x2U + +#define XRFDC_ODD_NYQUIST_ZONE 0x1U +#define XRFDC_EVEN_NYQUIST_ZONE 0x2U + +#define XRFDC_INTERP_DECIM_OFF 0x0U +#define XRFDC_INTERP_DECIM_1X 0x1U +#define XRFDC_INTERP_DECIM_2X 0x2U +#define XRFDC_INTERP_DECIM_3X 0x3U +#define XRFDC_INTERP_DECIM_4X 0x4U +#define XRFDC_INTERP_DECIM_5X 0x5U +#define XRFDC_INTERP_DECIM_6X 0x6U +#define XRFDC_INTERP_DECIM_8X 0x8U +#define XRFDC_INTERP_DECIM_10X 0xAU +#define XRFDC_INTERP_DECIM_12X 0xCU +#define XRFDC_INTERP_DECIM_16X 0x10U +#define XRFDC_INTERP_DECIM_20X 0x14U +#define XRFDC_INTERP_DECIM_24X 0x18U +#define XRFDC_INTERP_DECIM_40X 0x28U + +#define XRFDC_FAB_CLK_DIV1 0x1U +#define XRFDC_FAB_CLK_DIV2 0x2U +#define XRFDC_FAB_CLK_DIV4 0x3U +#define XRFDC_FAB_CLK_DIV8 0x4U +#define XRFDC_FAB_CLK_DIV16 0x5U + +#define XRFDC_CALIB_MODE1 0x1U +#define XRFDC_CALIB_MODE2 0x2U +#define XRFDC_TI_DCB_MODE1_4GSPS 0x00007800U +#define XRFDC_TI_DCB_MODE1_2GSPS 0x00005000U + +/* PLL Configuration */ +#define XRFDC_PLL_UNLOCKED 0x1U +#define XRFDC_PLL_LOCKED 0x2U + +#define XRFDC_EXTERNAL_CLK 0x0U +#define XRFDC_INTERNAL_PLL_CLK 0x1U + +#define PLL_FPDIV_MIN 13U +#define PLL_FPDIV_MAX 128U +#define PLL_DIVIDER_MIN 2U +#define PLL_DIVIDER_MAX 28U +#define VCO_RANGE_MIN 8500U +#define VCO_RANGE_MAX 13200U +#define XRFDC_PLL_LPF1_VAL 0x6U +#define XRFDC_PLL_CRS2_VAL 0x7008U +#define XRFDC_VCO_UPPER_BAND 0x0U +#define XRFDC_VCO_LOWER_BAND 0x1U +#define XRFDC_REF_CLK_DIV_1 0x1U +#define XRFDC_REF_CLK_DIV_2 0x2U +#define XRFDC_REF_CLK_DIV_3 0x3U +#define XRFDC_REF_CLK_DIV_4 0x4U + +#define XRFDC_SINGLEBAND_MODE 0x1U +#define XRFDC_MULTIBAND_MODE_2X 0x2U +#define XRFDC_MULTIBAND_MODE_4X 0x4U + +#define XRFDC_MB_DATATYPE_C2C 0x1U +#define XRFDC_MB_DATATYPE_R2C 0x2U +#define XRFDC_MB_DATATYPE_C2R 0x4U + +#define XRFDC_SB_C2C_BLK0 0x82U +#define XRFDC_SB_C2C_BLK1 0x64U +#define XRFDC_SB_C2R 0x40U +#define XRFDC_MB_C2C_BLK0 0x5EU +#define XRFDC_MB_C2C_BLK1 0x5DU +#define XRFDC_MB_C2R_BLK0 0x5CU +#define XRFDC_MB_C2R_BLK1 0x0U + +#define XRFDC_MIXER_MODE_BYPASS 0x2U + +#define XRFDC_LINK_COUPLING_DC 0x0U +#define XRFDC_LINK_COUPLING_AC 0x1U + +#define XRFDC_MB_MODE_SB 0x0U +#define XRFDC_MB_MODE_2X_BLK01 0x1U +#define XRFDC_MB_MODE_2X_BLK23 0x2U +#define XRFDC_MB_MODE_2X_BLK01_BLK23 0x3U +#define XRFDC_MB_MODE_4X 0x4U + +#define XRFDC_MILLI 1000U +#define XRFDC_DAC_SAMPLING_MIN 500 +#define XRFDC_DAC_SAMPLING_MAX 6554 +#define XRFDC_ADC_4G_SAMPLING_MIN 1000 +#define XRFDC_ADC_4G_SAMPLING_MAX 4116 +#define XRFDC_ADC_2G_SAMPLING_MIN 500 +#define XRFDC_ADC_2G_SAMPLING_MAX 2058 +#define XRFDC_REFFREQ_MIN 102.40625 +#define XRFDC_REFFREQ_MAX 614 + +#define XRFDC_DIGITALPATH_ENABLE 0x1U +#define XRFDC_ANALOGPATH_ENABLE 0x1U + +#define XRFDC_BLK_ID0 0x0U +#define XRFDC_BLK_ID1 0x1U +#define XRFDC_BLK_ID2 0x2U +#define XRFDC_BLK_ID3 0x3U +#define XRFDC_BLK_ID4 0x4U + +#define XRFDC_TILE_ID0 0x0U +#define XRFDC_TILE_ID1 0x1U +#define XRFDC_TILE_ID2 0x2U +#define XRFDC_TILE_ID3 0x3U +#define XRFDC_TILE_ID4 0x4U + +#define XRFDC_NUM_OF_BLKS1 0x1U +#define XRFDC_NUM_OF_BLKS2 0x2U +#define XRFDC_NUM_OF_BLKS3 0x3U +#define XRFDC_NUM_OF_BLKS4 0x4U + +#define XRFDC_NUM_OF_TILES1 0x1U +#define XRFDC_NUM_OF_TILES2 0x2U +#define XRFDC_NUM_OF_TILES3 0x3U +#define XRFDC_NUM_OF_TILES4 0x4U + +#define XRFDC_SM_STATE0 0x0U +#define XRFDC_SM_STATE1 0x1U +#define XRFDC_SM_STATE15 0xFU + +#define XRFDC_DECIM_4G_DATA_TYPE 0x3U +#define XRFDC_DECIM_2G_IQ_DATA_TYPE 0x2U + +#define XRFDC_DAC_MAX_WR_FAB_RATE 16U +#define XRFDC_ADC_MAX_RD_FAB_RATE 8U + +#define XRFDC_MIN_PHASE_CORR_FACTOR -26.5 +#define XRFDC_MAX_PHASE_CORR_FACTOR 26.5 +#define XRFDC_MAX_GAIN_CORR_FACTOR 2.0 +#define XRFDC_MIN_GAIN_CORR_FACTOR 0.0 + +#define XRFDC_FAB_RATE_8 8 +#define XRFDC_FAB_RATE_4 4 +#define XRFDC_FAB_RATE_2 2 +#define XRFDC_FAB_RATE_1 1 + +#define XRFDC_HSCOM_PWR_STATS_PLL 0xFFC0U +#define XRFDC_HSCOM_PWR_STATS_EXTERNAL 0xF240U + +#define XRFDC_CLK_DST_DAC0 0 +#define XRFDC_CLK_DST_DAC1 1 +#define XRFDC_CLK_DST_DAC2 2 +#define XRFDC_CLK_DST_DAC3 3 +#define XRFDC_CLK_DST_ADC0 4 +#define XRFDC_CLK_DST_ADC1 5 +#define XRFDC_CLK_DST_ADC2 6 +#define XRFDC_CLK_DST_ADC3 7 +#define XRFDC_CLK_DST_INVALID 0xFFU + +#define XRFDC_CLK_DISTR_MUX4A_SRC_INT 0x0008U +#define XRFDC_CLK_DISTR_MUX4A_SRC_STH 0x0000U +#define XRFDC_CLK_DISTR_MUX6_SRC_OFF 0x0000U +#define XRFDC_CLK_DISTR_MUX6_SRC_INT 0x0100U +#define XRFDC_CLK_DISTR_MUX6_SRC_NTH 0x0080U +#define XRFDC_CLK_DISTR_MUX7_SRC_OFF 0x0000U +#define XRFDC_CLK_DISTR_MUX7_SRC_STH 0x0200U +#define XRFDC_CLK_DISTR_MUX7_SRC_INT 0x0400U +#define XRFDC_CLK_DISTR_MUX8_SRC_NTH 0x0000U +#define XRFDC_CLK_DISTR_MUX8_SRC_INT 0x8000U +#define XRFDC_CLK_DISTR_MUX9_SRC_NTH 0x4000U +#define XRFDC_CLK_DISTR_MUX9_SRC_INT 0x0000U +#define XRFDC_CLK_DISTR_MUX5A_SRC_PLL 0x0800U +#define XRFDC_CLK_DISTR_MUX5A_SRC_RX 0x0040U +#define XRFDC_CLK_DISTR_OFF (XRFDC_CLK_DISTR_MUX4A_SRC_INT | \ + XRFDC_CLK_DISTR_MUX6_SRC_OFF | \ + XRFDC_CLK_DISTR_MUX7_SRC_OFF | \ + XRFDC_CLK_DISTR_MUX8_SRC_NTH | \ + XRFDC_CLK_DISTR_MUX9_SRC_INT) +#define XRFDC_CLK_DISTR_LEFTMOST_TILE 0x0000U +#define XRFDC_CLK_DISTR_CONT_LEFT_EVEN 0x8208U +#define XRFDC_CLK_DISTR_CONT_LEFT_ODD 0x8200U +#define XRFDC_CLK_DISTR_RIGHTMOST_TILE 0x4008 +#define XRFDC_CLK_DISTR_CONT_RIGHT_EVEN 0x4080 +#define XRFDC_CLK_DISTR_CONT_RIGHT_HWL_ODD 0x4088 + +#define XRFDC_CLK_DISTR_MUX4A_SRC_CLR 0x0008U +#define XRFDC_CLK_DISTR_MUX6_SRC_CLR 0x0180U +#define XRFDC_CLK_DISTR_MUX7_SRC_CLR 0x0600U +#define XRFDC_CLK_DISTR_MUX8_SRC_CLR 0x8000U +#define XRFDC_CLK_DISTR_MUX9_SRC_CLR 0x4000U + +#define XRFDC_DIST_MAX 8 + +#define XRFDC_NET_CTRL_CLK_REC_PLL 0x1U +#define XRFDC_NET_CTRL_CLK_REC_DIST_T1 0x2U +#define XRFDC_NET_CTRL_CLK_T1_SRC_LOCAL 0x4U +#define XRFDC_NET_CTRL_CLK_T1_SRC_DIST 0x8U +#define XRFDC_NET_CTRL_CLK_INPUT_DIST 0x20U +#define XRFDC_DIST_CTRL_TO_PLL_DIV 0x10U +#define XRFDC_DIST_CTRL_TO_T1 0x20U +#define XRFDC_DIST_CTRL_DIST_SRC_LOCAL 0x40U +#define XRFDC_DIST_CTRL_DIST_SRC_PLL 0x800U +#define XRFDC_DIST_CTRL_CLK_T1_SRC_LOCAL 0x1000U +#define XRFDC_PLLREFDIV_INPUT_OFF 0x20U +#define XRFDC_PLLREFDIV_INPUT_DIST 0x40U +#define XRFDC_PLLREFDIV_INPUT_FABRIC 0x60U +#define XRFDC_PLLOPDIV_INPUT_DIST_LOCAL 0x800U + +#define XRFDC_TILE_SOURCE_RX 0U +#define XRFDC_TILE_SOURCE_DIST 1U +#define XRFDC_TILE_SOURCE_FABRIC 2U + +#define XRFDC_DIST_OUT_NONE 0U +#define XRFDC_DIST_OUT_RX 1U +#define XRFDC_DIST_OUT_OUTDIV 2U + +#define XRFDC_PLL_SOURCE_NONE 0U +#define XRFDC_PLL_SOURCE_RX 1U +#define XRFDC_PLL_SOURCE_OUTDIV 2U + +#define XRFDC_PLL_OUTDIV_MODE_1 0x0U +#define XRFDC_PLL_OUTDIV_MODE_2 0x1U +#define XRFDC_PLL_OUTDIV_MODE_3 0x2U +#define XRFDC_PLL_OUTDIV_MODE_N 0x3U + +#define XRFDC_PLL_OUTDIV_MODE_3_VAL 0x1U + +#define XRFDC_DIVISION_FACTOR_MIN 1 + +#define XRFDC_DITH_ENABLE 1 +#define XRFDC_DITH_DISABLE 0 + +#define XRFDC_SIGDET_MODE_AVG 0 +#define XRFDC_SIGDET_MODE_RNDM 1 +#define XRFDC_SIGDET_TC_2_0 0 +#define XRFDC_SIGDET_TC_2_2 1 +#define XRFDC_SIGDET_TC_2_4 2 +#define XRFDC_SIGDET_TC_2_8 3 +#define XRFDC_SIGDET_TC_2_12 4 +#define XRFDC_SIGDET_TC_2_14 5 +#define XRFDC_SIGDET_TC_2_16 6 +#define XRFDC_SIGDET_TC_2_18 7 + +#define XRFDC_DISABLED 0 +#define XRFDC_ENABLED 1 + +#define XRFDC_CAL_BLOCK_OCB1 0 +#define XRFDC_CAL_BLOCK_OCB2 1 +#define XRFDC_CAL_BLOCK_GCB 2 +#define XRFDC_CAL_BLOCK_TSCB 3 + +#define XRFDC_INV_SYNC_MODE_MAX 2 + +#define XRFDC_INV_SYNC_EN_MAX 1 + + +#define XRFDC_CTRL_MASK 0x4800 +#define XRFDC_EXPORTCTRL_CLKDIST 0x4000 +#define XRFDC_PREMIUMCTRL_CLKDIST 0x0800 + +#define XRFDC_DAC_MODE_7G_NQ1 0U +#define XRFDC_DAC_MODE_7G_NQ2 1U +#define XRFDC_DAC_MODE_10G_IMR 2U +#define XRFDC_DAC_MODE_10G_BYPASS 3U +#define XRFDC_DAC_MODE_MAX XRFDC_DAC_MODE_10G_BYPASS + +#define XRFDC_DAC_IMR_MODE_LOWPASS 0U +#define XRFDC_DAC_IMR_MODE_HIGHPASS 1U +#define XRFDC_DAC_IMR_MODE_MAX XRFDC_DAC_IMR_MODE_HIGHPASS + +#define XRFDC_CLOCK_DETECT_CLK 0x1U +#define XRFDC_CLOCK_DETECT_DIST 0x2U +#define XRFDC_CLOCK_DETECT_BOTH 0x3U + +#define XRFDC_CAL_UNFREEZE_CALIB 0U +#define XRFDC_CAL_FREEZE_CALIB 1U +#define XRFDC_CAL_FRZ_PIN_ENABLE 0U +#define XRFDC_CAL_FRZ_PIN_DISABLE 1U +/*****************************************************************************/ +/** +* +* Execute Read modify Write +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddr is address of a block. +* @param RegAddr is register offset value. +* @param Mask contains bit mask value. +* @param Data contains value to be written to register. +* +* @return +* - None +* +******************************************************************************/ +static inline void XRFdc_ClrSetReg(XRFdc *InstancePtr, u32 BaseAddr, + u32 RegAddr, u16 Mask, u16 Data) +{ + u16 ReadReg; + + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, RegAddr); + ReadReg = (ReadReg & ~Mask) | (Data & Mask); + XRFdc_WriteReg16(InstancePtr, BaseAddr, RegAddr, ReadReg); +} + +/*****************************************************************************/ +/** +* +* Execute Read and clear +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddr is address of a block. +* @param RegAddr is register offset value. +* @param Mask contains bit mask value. +* +* @return +* - None +* +******************************************************************************/ +static inline void XRFdc_ClrReg(XRFdc *InstancePtr, u32 BaseAddr, + u32 RegAddr, u16 Mask) +{ + u16 ReadReg; + + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, RegAddr); + ReadReg &= ~Mask; + XRFdc_WriteReg16(InstancePtr, BaseAddr, RegAddr, ReadReg); +} + +/*****************************************************************************/ +/** +* +* Execute Read and mask with the value +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddr is address of a block. +* @param RegAddr is register offset value. +* @param Mask contains bit mask value. +* +* @return +* - None +* +******************************************************************************/ +static inline u16 XRFdc_RDReg(XRFdc *InstancePtr, u32 BaseAddr, + u32 RegAddr, u16 Mask) +{ + u16 ReadReg; + + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, RegAddr); + ReadReg &= Mask; + + return ReadReg; +} + +/*****************************************************************************/ +/** +* +* Checks whether DAC block is available or not. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - Return 1 if DAC block is available, otherwise 0. +* +******************************************************************************/ +static inline u32 XRFdc_IsDACBlockEnabled(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id) +{ + return InstancePtr->RFdc_Config.DACTile_Config[Tile_Id]. + DACBlock_Analog_Config[Block_Id].BlockAvailable; +} + +/*****************************************************************************/ +/** +* +* Checks whether ADC block is available or not. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3 in DAC/ADC-2GSPS and 0-1 in ADC-4GSPS. +* +* @return +* - Return 1 if ADC block is available, otherwise 0. +* +******************************************************************************/ +static inline u32 XRFdc_IsADCBlockEnabled(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id) +{ + u32 IsBlockAvail; + + if (InstancePtr->RFdc_Config.ADCType == XRFDC_ADC_4GSPS) { + if ((Block_Id == 2U) || (Block_Id == 3U)) { + IsBlockAvail = 0; + goto RETURN_PATH; + } + if (Block_Id == 1U) { + Block_Id = 2U; + } + } + IsBlockAvail = InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id]. + ADCBlock_Analog_Config[Block_Id].BlockAvailable; +RETURN_PATH: + return IsBlockAvail; +} + +/*****************************************************************************/ +/** +* +* Checks whether DAC Digital path is enabled or not. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - Return 1 if DAC digital path is enabled, otherwise 0. +* +******************************************************************************/ +static inline u32 XRFdc_IsDACDigitalPathEnabled(XRFdc *InstancePtr, + u32 Tile_Id, u32 Block_Id) +{ + u32 Status; + + if (InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[Block_Id]. + Mixer_Settings.MixerType == XRFDC_MIXER_TYPE_OFF) { + Status = 0U; + } else { + Status = 1U; + } + + return Status; +} + +/*****************************************************************************/ +/** +* +* Checks whether ADC digital path is enabled or not. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3 in DAC/ADC-2GSPS and 0-1 in ADC-4GSPS. +* +* @return +* - Return 1 if ADC digital path is enabled, otherwise 0. +* +******************************************************************************/ +static inline u32 XRFdc_IsADCDigitalPathEnabled(XRFdc *InstancePtr, + u32 Tile_Id, u32 Block_Id) +{ + u32 IsBlockAvail; + + if (InstancePtr->RFdc_Config.ADCType == XRFDC_ADC_4GSPS) { + if ((Block_Id == 2U) || (Block_Id == 3U)) { + IsBlockAvail = 0; + goto RETURN_PATH; + } + if (Block_Id == 1U) { + Block_Id = 2U; + } + } + + if (InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Block_Id]. + Mixer_Settings.MixerType == XRFDC_MIXER_TYPE_OFF) { + IsBlockAvail = 0; + } else { + IsBlockAvail = 1; + } + +RETURN_PATH: + return IsBlockAvail; +} + +/*****************************************************************************/ +/** +* +* Checks whether ADC/DAC Digital path is enabled or not. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - XRFDC_SUCCESS if Digital path is enabled. +* - XRFDC_FAILURE if Digital path is not enabled. +* +******************************************************************************/ +static inline u32 XRFdc_CheckDigitalPathEnabled(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id) +{ + u32 IsBlockAvail; + u32 Status; + + if ((Type != XRFDC_ADC_TILE) && (Type != XRFDC_DAC_TILE)) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((Tile_Id > XRFDC_TILE_ID_MAX) || (Block_Id > XRFDC_BLOCK_ID_MAX)) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (Type == XRFDC_ADC_TILE) { + IsBlockAvail = XRFdc_IsADCDigitalPathEnabled(InstancePtr, Tile_Id, + Block_Id); + } else { + IsBlockAvail = XRFdc_IsDACDigitalPathEnabled(InstancePtr, Tile_Id, + Block_Id); + } + if (IsBlockAvail == 0U) { + Status = XRFDC_FAILURE; + } else { + Status = XRFDC_SUCCESS; + } +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Get IP BaseAddress. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* +* @return +* - Return IP BaseAddress. +* +******************************************************************************/ +static inline u32 XRFdc_Get_IPBaseAddr(XRFdc *InstancePtr) +{ + return (u32)InstancePtr->BaseAddr; +} + +/*****************************************************************************/ +/** +* +* Get Tile BaseAddress +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* +* @return +* - Return Tile BaseAddress. +* +******************************************************************************/ +static inline u32 XRFdc_Get_TileBaseAddr(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id) +{ + u32 BaseAddr; + + if (Type == XRFDC_ADC_TILE) { + BaseAddr = InstancePtr->BaseAddr + XRFDC_ADC_TILE_DRP_ADDR(Tile_Id); + } else { + BaseAddr = InstancePtr->BaseAddr + XRFDC_DAC_TILE_DRP_ADDR(Tile_Id); + } + + return BaseAddr; +} + +/*****************************************************************************/ +/** +* +* Get Block BaseAddress +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3 in DAC/ADC-2GSPS and 0-1 in ADC-4GSPS. +* +* @return +* - Return Block BaseAddress. +* +******************************************************************************/ +static inline u32 XRFdc_Get_BlockBaseAddr(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id) +{ + u32 BaseAddr; + + if (Type == XRFDC_ADC_TILE) { + if (InstancePtr->RFdc_Config.ADCType == XRFDC_ADC_4GSPS) { + if (Block_Id == 1U) { + Block_Id = 2U; + } + } + BaseAddr = InstancePtr->BaseAddr + XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Block_Id); + } else { + BaseAddr = InstancePtr->BaseAddr + XRFDC_DAC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Block_Id); + } + + return BaseAddr; +} + +/*****************************************************************************/ +/** +* +* Get Number of DAC Blocks enabled. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* +* @return +* - Return number of DAC blocks enabled. +* +******************************************************************************/ +static inline u32 XRFdc_GetNoOfDACBlock(XRFdc *InstancePtr, u32 Tile_Id) +{ + return InstancePtr->DAC_Tile[Tile_Id].NumOfDACBlocks; +} + +/*****************************************************************************/ +/** +* +* Get Number of ADC Blocks enabled. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* +* @return +* - Return number of ADC blocks enabled. +* +******************************************************************************/ +static inline u32 XRFdc_GetNoOfADCBlocks(XRFdc *InstancePtr, u32 Tile_Id) +{ + return InstancePtr->ADC_Tile[Tile_Id].NumOfADCBlocks; +} + +/*****************************************************************************/ +/** +* +* Get ADC type is High Speed or Medium Speed. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* +* @return +* - Return 1 if ADC type is 4GSPS, otherwise 0. +* +******************************************************************************/ + +static inline u32 XRFdc_IsHighSpeedADC(XRFdc *InstancePtr, int Tile) +{ + if (InstancePtr->RFdc_Config.ADCTile_Config[Tile].NumSlices == 0) { + return InstancePtr->ADC4GSPS; + } else { + return (InstancePtr->RFdc_Config.ADCTile_Config[Tile].NumSlices == XRFDC_NUM_SLICES_HSADC); + } +} + +/*****************************************************************************/ +/** +* +* Get Mixer Input Data Type for ADC/DAC block. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - Return MixerInputDataType of ADC/DAC block. +* +******************************************************************************/ +static inline u32 XRFdc_GetDataType(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id) +{ + u32 MixerInputDataType; + + if (Type == XRFDC_ADC_TILE) { + MixerInputDataType = InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id]. + ADCBlock_Digital_Config[Block_Id].MixerInputDataType; + } else { + MixerInputDataType = InstancePtr->RFdc_Config.DACTile_Config[Tile_Id]. + DACBlock_Digital_Config[Block_Id].MixerInputDataType; + } + + return MixerInputDataType; +} + +/*****************************************************************************/ +/** +* +* Get Data Width for ADC/DAC block. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - Return DataWidth of ADC/DAC block. +* +******************************************************************************/ +static inline u32 XRFdc_GetDataWidth(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id) +{ + u32 DataWidth; + + if (Type == XRFDC_ADC_TILE) { + DataWidth = InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id]. + ADCBlock_Digital_Config[Block_Id].DataWidth; + } else { + DataWidth = InstancePtr->RFdc_Config.DACTile_Config[Tile_Id]. + DACBlock_Digital_Config[Block_Id].DataWidth; + } + + return DataWidth; +} + +/*****************************************************************************/ +/** +* +* Get Inversesync filter for DAC block. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - Return Inversesync filter for DAC block +* +******************************************************************************/ +static inline u32 XRFdc_GetInverseSincFilter(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id) +{ + return InstancePtr->RFdc_Config.DACTile_Config[Tile_Id]. + DACBlock_Analog_Config[Block_Id].InvSyncEnable; +} + +/*****************************************************************************/ +/** +* +* Get Mixed mode for DAC block. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - Return mixed mode for DAC block +* +******************************************************************************/ +static inline u32 XRFdc_GetMixedMode(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id) +{ + return InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Analog_Datapath[Block_Id].MixedMode; +} + +/*****************************************************************************/ +/** +* +* Get Master Tile for ADC/DAC tiles. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* +* @return +* - Return Master Tile for ADC/DAC tiles +* +******************************************************************************/ +static inline u32 XRFdc_GetMasterTile(XRFdc *InstancePtr, u32 Type) +{ + u32 MasterTile; + + if (Type == XRFDC_ADC_TILE) { + MasterTile = InstancePtr->RFdc_Config.MasterADCTile; + } else { + MasterTile = InstancePtr->RFdc_Config.MasterDACTile; + } + + return MasterTile; +} + +/*****************************************************************************/ +/** +* +* Get Sysref source for ADC/DAC tile. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* +* @return +* - Return Sysref source for ADC/DAC tile +* +******************************************************************************/ +static inline u32 XRFdc_GetSysRefSource(XRFdc *InstancePtr, u32 Type) +{ + u32 SysRefSource; + + if (Type == XRFDC_ADC_TILE) { + SysRefSource = InstancePtr->RFdc_Config.ADCSysRefSource; + } else { + SysRefSource = InstancePtr->RFdc_Config.DACSysRefSource; + } + + return SysRefSource; +} + +/*****************************************************************************/ +/** +* +* Get Fabric Clock frequency. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* +* @return +* - Return Fabric Clock frequency for ADC/DAC tile +* +******************************************************************************/ +static inline double XRFdc_GetFabClkFreq(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id) +{ + double FabClkFreq; + + if (Type == XRFDC_ADC_TILE) { + FabClkFreq = InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].FabClkFreq; + } else { + FabClkFreq = InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].FabClkFreq; + } + + return FabClkFreq; +} + +/*****************************************************************************/ +/** +* +* Get whether FIFO is enabled or not. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - Return 1 if FIFO is enabled, otherwise 0. +* +******************************************************************************/ +static inline u32 XRFdc_IsFifoEnabled(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id) +{ + u32 FifoEnable; + + if (Type == XRFDC_ADC_TILE) { + FifoEnable = InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id]. + ADCBlock_Digital_Config[Block_Id].FifoEnable; + } else { + FifoEnable = InstancePtr->RFdc_Config.DACTile_Config[Tile_Id]. + DACBlock_Digital_Config[Block_Id].FifoEnable; + } + + return FifoEnable; +} + +/*****************************************************************************/ +/** +* +* Get Data Converter connected for digital data path I +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is Digital Data Path number. +* +* @return +* - Return Data converter Id. +* +******************************************************************************/ +static inline int XRFdc_GetConnectedIData(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id) +{ + int ConnectedIData; + + if (Type == XRFDC_ADC_TILE) { + ConnectedIData = InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].ConnectedIData; + } else { + ConnectedIData = InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Block_Id].ConnectedIData; + } + + return ConnectedIData; +} + +/*****************************************************************************/ +/** +* +* Get Data Converter connected for digital data path Q +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is Digital Data Path number. +* +* @return +* - Return Data converter Id. +* +******************************************************************************/ +static inline int XRFdc_GetConnectedQData(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id) +{ + int ConnectedQData; + + if (Type == XRFDC_ADC_TILE) { + ConnectedQData = InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].ConnectedQData; + } else { + ConnectedQData = InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Block_Id].ConnectedQData; + } + + return ConnectedQData; +} + +/*****************************************************************************/ +/** +* +* Set Data Converter connected for digital data path I and Q +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is Digital Data Path number. +* @param ConnectedIData is Converter Id to which DigitalPathI connected. +* @param ConnectedQData is Converter Id to which DigitalPathQ connected. +* +* @return +* - None. +* +******************************************************************************/ +static inline void XRFdc_SetConnectedIQData(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id, int ConnectedIData, int ConnectedQData) +{ + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].ConnectedIData = ConnectedIData; + InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].ConnectedQData = ConnectedQData; + } else { + InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Block_Id].ConnectedIData = ConnectedIData; + InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Block_Id].ConnectedQData = ConnectedQData; + } +} + +/*****************************************************************************/ +/** +* +* Get Multiband Config data +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* +* @return +* - Return Multiband Configuration. +* +******************************************************************************/ +static inline u32 XRFdc_GetMultibandConfig(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id) +{ + u32 MultibandConfig; + + if (Type == XRFDC_ADC_TILE) { + MultibandConfig = InstancePtr->ADC_Tile[Tile_Id].MultibandConfig; + } else { + MultibandConfig = InstancePtr->DAC_Tile[Tile_Id].MultibandConfig; + } + + return MultibandConfig; +} + +/*****************************************************************************/ +/** +* +* Checks whether ADC/DAC block is enabled or not. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - XRFDC_SUCCESS if block enabled. +* - XRFDC_FAILURE if Block not enabled. +* +******************************************************************************/ +static inline u32 XRFdc_CheckBlockEnabled(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id) +{ + u32 IsBlockAvail; + u32 Status; + + if ((Type != XRFDC_ADC_TILE) && (Type != XRFDC_DAC_TILE)) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((Tile_Id > XRFDC_TILE_ID_MAX) || (Block_Id > XRFDC_BLOCK_ID_MAX)) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (Type == XRFDC_ADC_TILE) { + IsBlockAvail = XRFdc_IsADCBlockEnabled(InstancePtr, Tile_Id, Block_Id); + } else { + IsBlockAvail = XRFdc_IsDACBlockEnabled(InstancePtr, Tile_Id, Block_Id); + } + if (IsBlockAvail == 0U) { + Status = XRFDC_FAILURE; + } else { + Status = XRFDC_SUCCESS; + } +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Checks whether ADC/DAC tile is enabled or not. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC. +* @param Tile_Id Valid values are 0-3. +* +* @return +* - XRFDC_SUCCESS if tile enabled. +* - XRFDC_FAILURE if tile not enabled. +* +******************************************************************************/ +static inline u32 XRFdc_CheckTileEnabled(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id) +{ + u32 IsTileAvail; + u32 Status; + + if ((Type != XRFDC_ADC_TILE) && (Type != XRFDC_DAC_TILE)) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (Tile_Id > XRFDC_TILE_ID_MAX) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (Type == XRFDC_ADC_TILE) { + IsTileAvail = InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].Enable; + } else { + IsTileAvail = InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].Enable; + } + if (IsTileAvail == 0U) { + Status = XRFDC_FAILURE; + } else { + Status = XRFDC_SUCCESS; + } +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* Gets ADC/DAC tile maximum sampling rate. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC. +* @param Tile_Id Valid values are 0-3. +* @param MaxSampleRatePtr pointer for maximum sample rate. +* +* @return +* - XRFDC_SUCCESS if found sampling rate. +* - XRFDC_FAILURE if could not find sampling rate. +* +******************************************************************************/ +static inline u32 XRFdc_GetMaxSampleRate(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, double *MaxSampleRatePtr) +{ + u32 Status; + + if ((Type != XRFDC_ADC_TILE) && (Type != XRFDC_DAC_TILE)) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (Tile_Id > XRFDC_TILE_ID_MAX) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (Type == XRFDC_ADC_TILE) { + *MaxSampleRatePtr = InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].MaxSampleRate*1000; + if (*MaxSampleRatePtr == 0) { + *MaxSampleRatePtr = XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id)?XRFDC_ADC_4G_SAMPLING_MAX:XRFDC_ADC_2G_SAMPLING_MAX; + } + } else { + *MaxSampleRatePtr = InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].MaxSampleRate*1000; + if (*MaxSampleRatePtr == 0) { + *MaxSampleRatePtr = XRFDC_DAC_SAMPLING_MAX; + } + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* Gets ADC/DAC tile minimum sampling rate. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC. +* @param Tile_Id Valid values are 0-3. +* @param MinSampleRatePtr pointer for minimum sample rate. +* +* @return +* - XRFDC_SUCCESS if found sampling rate. +* - XRFDC_FAILURE if could not find sampling rate. +* +******************************************************************************/ +static inline u32 XRFdc_GetMinSampleRate(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, double *MinSampleRatePtr) +{ + u32 Status; + + if ((Type != XRFDC_ADC_TILE) && (Type != XRFDC_DAC_TILE)) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (Tile_Id > XRFDC_TILE_ID_MAX) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (Type == XRFDC_ADC_TILE) { + *MinSampleRatePtr = XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id)?XRFDC_ADC_4G_SAMPLING_MIN:XRFDC_ADC_2G_SAMPLING_MIN; + } else { + *MinSampleRatePtr = XRFDC_DAC_SAMPLING_MIN; + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This API is used to get the driver version. +* +* @param None +* +* @return +* Driver version number +* +* @note None +* +******************************************************************************/ +static inline double XRFdc_GetDriverVersion(void) +{ + return 6.0; +} + +/************************** Function Prototypes ******************************/ + +XRFdc_Config *XRFdc_LookupConfig(u16 DeviceId); +u32 XRFdc_CfgInitialize(XRFdc *InstancePtr, XRFdc_Config *ConfigPtr); +u32 XRFdc_StartUp(XRFdc *InstancePtr, u32 Type, int Tile_Id); +u32 XRFdc_Shutdown(XRFdc *InstancePtr, u32 Type, int Tile_Id); +u32 XRFdc_Reset(XRFdc *InstancePtr, u32 Type, int Tile_Id); +u32 XRFdc_GetIPStatus(XRFdc *InstancePtr, XRFdc_IPStatus *IPStatusPtr); +u32 XRFdc_GetBlockStatus(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_BlockStatus *BlockStatusPtr); +u32 XRFdc_SetMixerSettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_Mixer_Settings *MixerSettingsPtr); +u32 XRFdc_GetMixerSettings(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id, + XRFdc_Mixer_Settings *MixerSettingsPtr); +u32 XRFdc_SetQMCSettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_QMC_Settings *QMCSettingsPtr); +u32 XRFdc_GetQMCSettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_QMC_Settings *QMCSettingsPtr); +u32 XRFdc_GetCoarseDelaySettings(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id, + XRFdc_CoarseDelay_Settings *CoarseDelaySettingsPtr); +u32 XRFdc_SetCoarseDelaySettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_CoarseDelay_Settings *CoarseDelaySettingsPtr); +u32 XRFdc_GetInterpolationFactor(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id, u32 *InterpolationFactorPtr); +u32 XRFdc_GetDecimationFactor(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id, u32 *DecimationFactorPtr); +u32 XRFdc_GetFabWrVldWords(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id, u32 *FabricDataRatePtr); +u32 XRFdc_GetFabRdVldWords(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u32 Block_Id, u32 *FabricDataRatePtr); +u32 XRFdc_SetFabRdVldWords(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 FabricRdVldWords); +u32 XRFdc_SetFabWrVldWords(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 FabricWrVldWords); +u32 XRFdc_GetThresholdSettings(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id, XRFdc_Threshold_Settings *ThresholdSettingsPtr); +u32 XRFdc_SetThresholdSettings(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + XRFdc_Threshold_Settings *ThresholdSettingsPtr); +u32 XRFdc_SetDecoderMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 DecoderMode); +u32 XRFdc_UpdateEvent(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, u32 Block_Id, + u32 Event); +u32 XRFdc_GetDecoderMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *DecoderModePtr); +u32 XRFdc_ResetNCOPhase(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id); +void XRFdc_DumpRegs(XRFdc *InstancePtr, u32 Type, int Tile_Id); +u32 XRFdc_MultiBand(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 DigitalDataPathMask, u32 MixerInOutDataType, u32 DataConverterMask); +u32 XRFdc_IntrHandler(u32 Vector, void *XRFdcPtr); +u32 XRFdc_IntrClr(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 IntrMask); +u32 XRFdc_GetIntrStatus(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 *IntrStsPtr); +u32 XRFdc_IntrDisable(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 IntrMask); +u32 XRFdc_IntrEnable(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 IntrMask); +u32 XRFdc_SetThresholdClrMode(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id, u32 ThresholdToUpdate, u32 ClrMode); +u32 XRFdc_ThresholdStickyClear(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id, u32 ThresholdToUpdate); +void XRFdc_SetStatusHandler(XRFdc *InstancePtr, void *CallBackRef, + XRFdc_StatusHandler FunctionPtr); +u32 XRFdc_SetupFIFO(XRFdc *InstancePtr, u32 Type, int Tile_Id, u8 Enable); +u32 XRFdc_GetFIFOStatus(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, u8 *EnablePtr); +u32 XRFdc_SetNyquistZone(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 NyquistZone); +u32 XRFdc_GetNyquistZone(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 *NyquistZonePtr); +u32 XRFdc_GetOutputCurr(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id, u32 *OutputCurrPtr); +u32 XRFdc_SetDecimationFactor(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 DecimationFactor); +u32 XRFdc_SetInterpolationFactor(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 InterpolationFactor); +u32 XRFdc_SetFabClkOutDiv(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u16 FabClkDiv); +u32 XRFdc_SetCalibrationMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u8 CalibrationMode); +u32 XRFdc_GetCalibrationMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u8 *CalibrationModePtr); +u32 XRFdc_GetClockSource(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 *ClockSourcePtr); +u32 XRFdc_GetPLLLockStatus(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 *LockStatusPtr); +u32 XRFdc_GetPLLConfig(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, XRFdc_PLL_Settings *PLLSettings); +u32 XRFdc_DynamicPLLConfig(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 Source, double RefClkFreq, double SamplingRate); +u32 XRFdc_SetInvSincFIR(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, u16 Mode); +u32 XRFdc_GetInvSincFIR(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u16 *ModePtr); +u32 XRFdc_GetLinkCoupling(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *ModePtr); +u32 XRFdc_GetFabClkOutDiv(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u16 *FabClkDivPtr); +u32 XRFdc_SetDither(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, u32 Mode); +u32 XRFdc_GetDither(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, u32 *ModePtr); +u32 XRFdc_SetClkDistribution(XRFdc *InstancePtr, XRFdc_Distribution_Settings + *DistributionSettingsPtr); +u32 XRFdc_GetClkDistribution(XRFdc *InstancePtr, XRFdc_Distribution_Settings + *DistributionSettingsPtr); +u32 XRFdc_SetTileClkSettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + XRFdc_Tile_Clock_Settings *SettingsPtr); +u32 XRFdc_SetDataPathMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 Mode); +u32 XRFdc_GetDataPathMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *ModePtr); +u32 XRFdc_SetIMRPassMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 Mode); +u32 XRFdc_GetIMRPassMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *ModePtr); +u32 XRFdc_SetSignalDetector(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + XRFdc_Signal_Detector_Settings *SettingsPtr); +u32 XRFdc_GetSignalDetector(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + XRFdc_Signal_Detector_Settings *SettingsPtr); +u32 XRFdc_DisableCoefficientsOverride(XRFdc *InstancePtr, u32 Tile_Id, u32 + Block_Id, u32 CalibrationBlock); +u32 XRFdc_SetCalCoefficients(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 CalibrationBlock, XRFdc_Calibration_Coefficients *CoeffPtr); +u32 XRFdc_GetCalCoefficients(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 CalibrationBlock, XRFdc_Calibration_Coefficients *CoeffPtr); +u32 XRFdc_SetCalFreeze(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + XRFdc_Cal_Freeze_Settings *CalFreezePtr); +u32 XRFdc_GetCalFreeze(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + XRFdc_Cal_Freeze_Settings *CalFreezePtr); +#ifndef __BAREMETAL__ +s32 XRFdc_GetDeviceNameByDeviceId(char *DevNamePtr, u16 DevId); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* RFDC_H_ */ +/** @} */ diff --git a/mpm/include/mpm/rfdc/xrfdc_hw.h b/mpm/include/mpm/rfdc/xrfdc_hw.h new file mode 100644 index 000000000..f02bfdb2c --- /dev/null +++ b/mpm/include/mpm/rfdc/xrfdc_hw.h @@ -0,0 +1,2410 @@ +/****************************************************************************** +* +* Copyright (C) 2017-2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc_hw.h +* @addtogroup rfdc_v6_0 +* @{ +* +* This header file contains the identifiers and basic HW access driver +* functions (or macros) that can be used to access the device. Other driver +* functions are defined in xrfdc.h. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.0 sk 05/16/17 Initial release +* 2.1 sk 09/15/17 Remove Libmetal library dependency for MB. +* sk 09/21/17 Add support for Over voltage and Over +* Range interrupts. +* 2.3 sk 11/10/17 Corrected FIFO and DATA Interrupt masks. +* 2.4 sk 12/11/17 Added DDC and DUC support. +* 3.0 sg 13/01/18 Added PLL and external clock switch support +* 3.1 jm 01/24/18 Add Multi-tile sync support. +* sk 02/27/18 Add API's to configure Multiband. +* 4.0 sk 04/09/18 Removed redundant inclusion of xparameters.h file. +* 5.0 sk 08/03/18 Fixed MISRAC warnings. +* sk 08/24/18 Reorganize the code to improve readability and +* optimization. +* 5.1 cog 01/29/19 Added XRFdc_SetDither() and XRFdc_GetDither() APIs. +* 6.0 cog 02/17/19 New Interp/Decimation Mask. +* cog 02/17/19 Added new Inverse-Sinc mask. +* cog 02/17/19 Added new clock Distribution Defs. +* cog 02/17/19 Added new intratile clock Defs. +* cog 02/17/19 New Masks and offsets for XRFdc_GetPLLConfig() API. +* cog 02/17/19 New Masks and offsets for XRFdc_SetIMRPassMode() and +* XRFdc_SetIMRPassMode() APIs +* cog 02/17/19 New Masks and offsets for XRFdc_SetDACMode() and +* XRFdc_GetDACMode() APIs +* cog 02/17/19 New Masks and offsets for XRFdc_SetSignalDetector() and +* XRFdc_GetSignalDetector() APIs. +* cog 02/17/19 New Masks and offsets for XRFdc_DisableCoefficientsOverride(), +* XRFdc_SetCalCoefficients and XRFdc_GetCalCoefficients APIs. +* cog 02/19/19 New Masks and offsets for clock detection register. +* cog 02/20/19 New Masks for ADC common mode over/under voltage interrupts. +* cog 02/21/19 New Masks and offsets for XRFdc_SetCalFreeze() and +* XRFdc_GetCalFreeze() APIs. +* cog 03/25/19 The new common mode over/under voltage interrupts mask +* bits were clashing with other interrupt bits. +* cog 03/25/19 Added more calibration bypass masks. +* +*</pre> +* +******************************************************************************/ + +#ifndef RFDC_HW_H_ +#define RFDC_HW_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************** Include Files *********************************/ + +#ifdef __BAREMETAL__ +#include "xil_io.h" +#endif +#include "metal/io.h" +/************************** Constant Definitions *****************************/ + +/** @name Register Map + * + * Register offsets from the base address of an RFDC ADC and DAC device. + * @{ + */ + +#define XRFDC_CLK_EN_OFFSET 0x000U /**< ADC Clock Enable + Register */ +#define XRFDC_ADC_DEBUG_RST_OFFSET 0x004U /**< ADC Debug Reset + Register */ +#define XRFDC_ADC_FABRIC_RATE_OFFSET 0x008U /**< ADC Fabric Rate + Register */ +#define XRFDC_ADC_FABRIC_OFFSET 0x00CU /**< ADC Fabric Register */ +#define XRFDC_ADC_FABRIC_ISR_OFFSET 0x010U /**< ADC Fabric ISR + Register */ +#define XRFDC_DAC_FABRIC_ISR_OFFSET 0x014U /**< DAC Fabric ISR + Register */ +#define XRFDC_ADC_FABRIC_IMR_OFFSET 0x014U /**< ADC Fabric IMR + Register */ +#define XRFDC_DAC_FABRIC_IMR_OFFSET 0x018U /**< DAC Fabric IMR + Register */ +#define XRFDC_ADC_FABRIC_DBG_OFFSET 0x018U /**< ADC Fabric Debug + Register */ +#define XRFDC_ADC_UPDATE_DYN_OFFSET 0x01CU /**< ADC Update Dynamic + Register */ +#define XRFDC_DAC_UPDATE_DYN_OFFSET 0x020U /**< DAC Update Dynamic + Register */ +#define XRFDC_ADC_FIFO_LTNC_CRL_OFFSET 0x020U /**< ADC FIFO Latency + Control Register */ +#define XRFDC_ADC_DEC_ISR_OFFSET 0x030U /**< ADC Decoder interface + ISR Register */ +#define XRFDC_DAC_DATAPATH_OFFSET 0x034U /**< ADC Decoder interface + IMR Register */ +#define XRFDC_ADC_DEC_IMR_OFFSET 0x034U /**< ADC Decoder interface + IMR Register */ +#define XRFDC_DATPATH_ISR_OFFSET 0x038U /**< ADC Data Path + ISR Register */ +#define XRFDC_DATPATH_IMR_OFFSET 0x03CU /**< ADC Data Path + IMR Register */ +#define XRFDC_ADC_DECI_CONFIG_OFFSET 0x040U /**< ADC Decimation + Config Register */ +#define XRFDC_DAC_INTERP_CTRL_OFFSET 0x040U /**< DAC Interpolation + Control Register */ +#define XRFDC_ADC_DECI_MODE_OFFSET 0x044U /**< ADC Decimation mode + Register */ +#define XRFDC_DAC_ITERP_DATA_OFFSET 0x044U /**< DAC interpolation data */ +#define XRFDC_ADC_MXR_CFG0_OFFSET 0x080U /**< ADC I channel mixer + config Register */ +#define XRFDC_ADC_MXR_CFG1_OFFSET 0x084U /**< ADC Q channel mixer + config Register */ +#define XRFDC_MXR_MODE_OFFSET 0x088U /**< ADC/DAC mixer mode + Register */ +#define XRFDC_NCO_UPDT_OFFSET 0x08CU /**< ADC/DAC NCO Update + mode Register */ +#define XRFDC_NCO_RST_OFFSET 0x090U /**< ADC/DAC NCO Phase + Reset Register */ +#define XRFDC_ADC_NCO_FQWD_UPP_OFFSET 0x094U /**< ADC NCO Frequency + Word[47:32] Register */ +#define XRFDC_ADC_NCO_FQWD_MID_OFFSET 0x098U /**< ADC NCO Frequency + Word[31:16] Register */ +#define XRFDC_ADC_NCO_FQWD_LOW_OFFSET 0x09CU /**< ADC NCO Frequency + Word[15:0] Register */ +#define XRFDC_NCO_PHASE_UPP_OFFSET 0x0A0U /**< ADC/DAC NCO Phase[17:16] + Register */ +#define XRFDC_NCO_PHASE_LOW_OFFSET 0x0A4U /**< ADC/DAC NCO Phase[15:0] + Register */ +#define XRFDC_ADC_NCO_PHASE_MOD_OFFSET 0x0A8U /**< ADC NCO Phase + Mode Register */ +#define XRFDC_QMC_UPDT_OFFSET 0x0C8U /**< ADC/DAC QMC Update Mode + Register */ +#define XRFDC_QMC_CFG_OFFSET 0x0CCU /**< ADC/DAC QMC Config + Register */ +#define XRFDC_QMC_OFF_OFFSET 0x0D0U /**< ADC/DAC QMC Offset + Correction Register */ +#define XRFDC_QMC_GAIN_OFFSET 0x0D4U /**< ADC/DAC QMC Gain + Correction Register */ +#define XRFDC_QMC_PHASE_OFFSET 0x0D8U /**< ADC/DAC QMC Phase + Correction Register */ +#define XRFDC_ADC_CRSE_DLY_UPDT_OFFSET 0x0DCU /**< ADC Coarse Delay + Update Register */ +#define XRFDC_DAC_CRSE_DLY_UPDT_OFFSET 0x0E0U /**< DAC Coarse Delay + Update Register */ +#define XRFDC_ADC_CRSE_DLY_CFG_OFFSET 0x0E0U /**< ADC Coarse delay + Config Register */ +#define XRFDC_DAC_CRSE_DLY_CFG_OFFSET 0x0DCU /**< DAC Coarse delay + Config Register */ +#define XRFDC_ADC_DAT_SCAL_CFG_OFFSET 0x0E4U /**< ADC Data Scaling + Config Register */ +#define XRFDC_ADC_SWITCH_MATRX_OFFSET 0x0E8U /**< ADC Switch Matrix + Config Register */ +#define XRFDC_ADC_TRSHD0_CFG_OFFSET 0x0ECU /**< ADC Threshold0 + Config Register */ +#define XRFDC_ADC_TRSHD0_AVG_UP_OFFSET 0x0F0U /**< ADC Threshold0 + Average[31:16] Register */ +#define XRFDC_ADC_TRSHD0_AVG_LO_OFFSET 0x0F4U /**< ADC Threshold0 + Average[15:0] Register */ +#define XRFDC_ADC_TRSHD0_UNDER_OFFSET 0x0F8U /**< ADC Threshold0 + Under Threshold Register */ +#define XRFDC_ADC_TRSHD0_OVER_OFFSET 0x0FCU /**< ADC Threshold0 + Over Threshold Register */ +#define XRFDC_ADC_TRSHD1_CFG_OFFSET 0x100U /**< ADC Threshold1 + Config Register */ +#define XRFDC_ADC_TRSHD1_AVG_UP_OFFSET 0x104U /**< ADC Threshold1 + Average[31:16] Register */ +#define XRFDC_ADC_TRSHD1_AVG_LO_OFFSET 0x108U /**< ADC Threshold1 + Average[15:0] Register */ +#define XRFDC_ADC_TRSHD1_UNDER_OFFSET 0x10CU /**< ADC Threshold1 + Under Threshold Register */ +#define XRFDC_ADC_TRSHD1_OVER_OFFSET 0x110U /**< ADC Threshold1 + Over Threshold Register */ +#define XRFDC_ADC_FEND_DAT_CRL_OFFSET 0x140U /**< ADC Front end + Data Control Register */ +#define XRFDC_ADC_TI_DCB_CRL0_OFFSET 0x144U /**< ADC Time Interleaved + digital correction block gain control0 Register */ +#define XRFDC_ADC_TI_DCB_CRL1_OFFSET 0x148U /**< ADC Time Interleaved + digital correction block gain control1 Register */ +#define XRFDC_ADC_TI_DCB_CRL2_OFFSET 0x14CU /**< ADC Time Interleaved + digital correction block gain control2 Register */ +#define XRFDC_ADC_TI_DCB_CRL3_OFFSET 0x150U /**< ADC Time Interleaved + digital correction block gain control3 Register */ +#define XRFDC_ADC_TI_TISK_CRL0_OFFSET 0x154U /**< ADC Time skew correction + control bits0 Register */ +#define XRFDC_DAC_MC_CFG0_OFFSET 0x1C4U /**< Static Configuration + data for DAC Analog */ +#define XRFDC_ADC_TI_TISK_CRL1_OFFSET 0x158U /**< ADC Time skew correction + control bits1 Register */ +#define XRFDC_ADC_TI_TISK_CRL2_OFFSET 0x15CU /**< ADC Time skew correction + control bits2 Register */ +#define XRFDC_ADC_TI_TISK_CRL3_OFFSET 0x160U /**< ADC Time skew correction + control bits3 Register */ +#define XRFDC_ADC_TI_TISK_CRL4_OFFSET 0x164U /**< ADC Time skew correction + control bits4 Register */ +#define XRFDC_ADC_TI_TISK_DAC0_OFFSET 0x168U /**< ADC Time skew DAC + cal code of subadc ch0 Register */ +#define XRFDC_ADC_TI_TISK_DAC1_OFFSET 0x16CU /**< ADC Time skew DAC + cal code of subadc ch1 Register */ +#define XRFDC_ADC_TI_TISK_DAC2_OFFSET 0x170U /**< ADC Time skew DAC + cal code of subadc ch2 Register */ +#define XRFDC_ADC_TI_TISK_DAC3_OFFSET 0x174U /**< ADC Time skew DAC + cal code of subadc ch3 Register */ +#define XRFDC_ADC_TI_TISK_DACP0_OFFSET 0x178U /**< ADC Time skew DAC + cal code of subadc ch0 Register */ +#define XRFDC_ADC_TI_TISK_DACP1_OFFSET 0x17CU /**< ADC Time skew DAC + cal code of subadc ch1 Register */ +#define XRFDC_ADC_TI_TISK_DACP2_OFFSET 0x180U /**< ADC Time skew DAC + cal code of subadc ch2 Register */ +#define XRFDC_ADC_TI_TISK_DACP3_OFFSET 0x184U /**< ADC Time skew DAC + cal code of subadc ch3 Register */ +#define XRFDC_ADC0_SUBDRP_ADDR_OFFSET 0x198U /**< subadc0, sub-drp address + of target Register */ +#define XRFDC_ADC0_SUBDRP_DAT_OFFSET 0x19CU /**< subadc0, sub-drp data + of target Register */ +#define XRFDC_ADC1_SUBDRP_ADDR_OFFSET 0x1A0U /**< subadc1, sub-drp address + of target Register */ +#define XRFDC_ADC1_SUBDRP_DAT_OFFSET 0x1A4U /**< subadc1, sub-drp data + of target Register */ +#define XRFDC_ADC2_SUBDRP_ADDR_OFFSET 0x1A8U /**< subadc2, sub-drp address + of target Register */ +#define XRFDC_ADC2_SUBDRP_DAT_OFFSET 0x1ACU /**< subadc2, sub-drp data + of target Register */ +#define XRFDC_ADC3_SUBDRP_ADDR_OFFSET 0x1B0U /**< subadc3, sub-drp address + of target Register */ +#define XRFDC_ADC3_SUBDRP_DAT_OFFSET 0x1B4U /**< subadc3, sub-drp data + of target Register */ +#define XRFDC_ADC_RX_MC_PWRDWN_OFFSET 0x1C0U /**< ADC Static configuration + bits for ADC(RX) analog Register */ +#define XRFDC_ADC_DAC_MC_CFG0_OFFSET 0x1C4U /**< ADC/DAC Static + configuration bits for ADC/DAC analog Register */ +#define XRFDC_ADC_DAC_MC_CFG1_OFFSET 0x1C8U /**< ADC/DAC Static + configuration bits for ADC/DAC analog Register */ +#define XRFDC_ADC_DAC_MC_CFG2_OFFSET 0x1CCU /**< ADC/DAC Static + configuration bits for ADC/DAC analog Register */ +#define XRFDC_DAC_MC_CFG3_OFFSET 0x1D0U /**< DAC Static + configuration bits for DAC analog Register */ +#define XRFDC_ADC_RXPR_MC_CFG0_OFFSET 0x1D0U /**< ADC RX Pair static + Configuration Register */ +#define XRFDC_ADC_RXPR_MC_CFG1_OFFSET 0x1D4U /**< ADC RX Pair static + Configuration Register */ +#define XRFDC_ADC_TI_DCBSTS0_BG_OFFSET 0x200U /**< ADC DCB Status0 + BG Register */ +#define XRFDC_ADC_TI_DCBSTS0_FG_OFFSET 0x204U /**< ADC DCB Status0 + FG Register */ +#define XRFDC_ADC_TI_DCBSTS1_BG_OFFSET 0x208U /**< ADC DCB Status1 + BG Register */ +#define XRFDC_ADC_TI_DCBSTS1_FG_OFFSET 0x20CU /**< ADC DCB Status1 + FG Register */ +#define XRFDC_ADC_TI_DCBSTS2_BG_OFFSET 0x210U /**< ADC DCB Status2 + BG Register */ +#define XRFDC_ADC_TI_DCBSTS2_FG_OFFSET 0x214U /**< ADC DCB Status2 + FG Register */ +#define XRFDC_ADC_TI_DCBSTS3_BG_OFFSET 0x218U /**< ADC DCB Status3 + BG Register */ +#define XRFDC_ADC_TI_DCBSTS3_FG_OFFSET 0x21CU /**< ADC DCB Status3 + FG Register */ +#define XRFDC_ADC_TI_DCBSTS4_MB_OFFSET 0x220U /**< ADC DCB Status4 + MSB Register */ +#define XRFDC_ADC_TI_DCBSTS4_LB_OFFSET 0x224U /**< ADC DCB Status4 + LSB Register */ +#define XRFDC_ADC_TI_DCBSTS5_MB_OFFSET 0x228U /**< ADC DCB Status5 + MSB Register */ +#define XRFDC_ADC_TI_DCBSTS5_LB_OFFSET 0x22CU /**< ADC DCB Status5 + LSB Register */ +#define XRFDC_ADC_TI_DCBSTS6_MB_OFFSET 0x230U /**< ADC DCB Status6 + MSB Register */ +#define XRFDC_ADC_TI_DCBSTS6_LB_OFFSET 0x234U /**< ADC DCB Status6 + LSB Register */ +#define XRFDC_ADC_TI_DCBSTS7_MB_OFFSET 0x238U /**< ADC DCB Status7 + MSB Register */ +#define XRFDC_ADC_TI_DCBSTS7_LB_OFFSET 0x23CU /**< ADC DCB Status7 + LSB Register */ +#define XRFDC_ADC_FIFO_LTNCY_LB_OFFSET 0x280U /**< ADC FIFO Latency + measurement LSB Register */ +#define XRFDC_ADC_FIFO_LTNCY_MB_OFFSET 0x284U /**< ADC FIFO Latency + measurement MSB Register */ +#define XRFDC_DAC_DECODER_CTRL_OFFSET 0x180U /**< DAC Unary Decoder/ + Randomizer settings */ +#define XRFDC_DAC_DECODER_CLK_OFFSET 0x184U /**< Decoder Clock enable */ + +#define XRFDC_ADC_SIG_DETECT_CTRL_OFFSET 0x114 /**< ADC Signal Detector Control */ +#define XRFDC_ADC_SIG_DETECT_THRESHOLD0_LEVEL_OFFSET 0x118 /**< ADC Signal Detector Theshold 0 */ +#define XRFDC_ADC_SIG_DETECT_THRESHOLD0_CNT_ON_OFFSET 0x11C /**< ADC Signal Detector Theshold 0 on Counter */ +#define XRFDC_ADC_SIG_DETECT_THRESHOLD0_CNT_OFF_OFFSET 0x120 /**< ADC Signal Detector Theshold 0 off Counter */ +#define XRFDC_ADC_SIG_DETECT_THRESHOLD1_LEVEL_OFFSET 0x124 /**< ADC Signal Detector Theshold 1 */ +#define XRFDC_ADC_SIG_DETECT_THRESHOLD1_CNT_ON_OFFSET 0x128 /**< ADC Signal Detector Theshold 1 on Counter */ +#define XRFDC_ADC_SIG_DETECT_THRESHOLD1_CNT_OFF_OFFSET 0x12C /**< ADC Signal Detector Theshold 1 off Counter */ +#define XRFDC_ADC_SIG_DETECT_MAGN_OFFSET 0x130 /**< ADC Signal Detector Magintude */ + + +#define XRFDC_HSCOM_CLK_DSTR_OFFSET 0x088U /**< Clock Distribution Register*/ +#define XRFDC_HSCOM_CLK_DSTR_MASK 0xC788U /**< Clock Distribution Register*/ +#define XRFDC_HSCOM_CLK_DSTR_MASK_ALT 0x1870U /**< Clock Distribution Register + for Intratile*/ +#define XRFDC_HSCOM_PWR_OFFSET 0x094 /**< Control register during + power-up sequence */ +#define XRFDC_HSCOM_CLK_DIV_OFFSET 0xB0 /**< Fabric clk out divider */ +#define XRFDC_HSCOM_PWR_STATE_OFFSET 0xB4 /**< Check powerup state */ +#define XRFDC_HSCOM_UPDT_DYN_OFFSET 0x0B8 /**< Trigger the update + dynamic event */ +#define XRFDC_HSCOM_EFUSE_2_OFFSET 0x144 +#define XRFDC_DAC_INVSINC_OFFSET 0x0C0U /**< Invsinc control */ +#define XRFDC_DAC_MB_CFG_OFFSET 0x0C4U /**< Multiband config */ +#define XRFDC_MTS_SRDIST 0x1CA0U +#define XRFDC_MTS_SRCAP_T1 (0x24U << 2U) +#define XRFDC_MTS_SRCAP_PLL (0x0CU << 2U) +#define XRFDC_MTS_SRCAP_DIG (0x2CU << 2U) +#define XRFDC_MTS_SRDTC_T1 (0x27U << 2U) +#define XRFDC_MTS_SRDTC_PLL (0x26U << 2U) +#define XRFDC_MTS_SRFLAG (0x49U << 2U) +#define XRFDC_MTS_CLKSTAT (0x24U << 2U) +#define XRFDC_MTS_SRCOUNT_CTRL 0x004CU +#define XRFDC_MTS_SRCOUNT_VAL 0x0050U +#define XRFDC_MTS_SRFREQ_VAL 0x0054U +#define XRFDC_MTS_FIFO_CTRL_ADC 0x0010U +#define XRFDC_MTS_FIFO_CTRL_DAC 0x0014U +#define XRFDC_MTS_DELAY_CTRL 0x0028U +#define XRFDC_MTS_ADC_MARKER 0x0018U +#define XRFDC_MTS_ADC_MARKER_CNT 0x0010U +#define XRFDC_MTS_DAC_MARKER_CTRL 0x0048U +#define XRFDC_MTS_DAC_MARKER_CNT (0x92U << 2U) +#define XRFDC_MTS_DAC_MARKER_LOC (0x93U << 2U) + +#define XRFDC_RESET_OFFSET 0x00U /**< Tile reset register */ +#define XRFDC_RESTART_OFFSET 0x04U /**< Tile restart register */ +#define XRFDC_RESTART_STATE_OFFSET 0x08U /**< Tile restart state register */ +#define XRFDC_CURRENT_STATE_OFFSET 0x0CU /**< Current state register */ +#define XRFDC_CLOCK_DETECT_OFFSET 0x80U /**< Clock detect register */ +#define XRFDC_STATUS_OFFSET 0x228U /**< Common status register */ +#define XRFDC_COMMON_INTR_STS 0x100U /**< Common Intr Status register */ +#define XRFDC_COMMON_INTR_ENABLE 0x104U /**< Common Intr enable register */ +#define XRFDC_INTR_STS 0x200U /**< Intr status register */ +#define XRFDC_INTR_ENABLE 0x204U /**< Intr enable register */ +#define XRFDC_CONV_INTR_STS(X) (0x208U + (X * 0x08U)) +#define XRFDC_CONV_INTR_EN(X) (0x20CU + (X * 0x08U)) +#define XRFDC_CONV_CAL_STGS(X) (0x234U + (X * 0x04U)) +#define XRFDC_CAL_GCB_COEFF0_FAB(X) (0x280U + (X * 0x10U)) +#define XRFDC_CAL_GCB_COEFF1_FAB(X) (0x284U + (X * 0x10U)) +#define XRFDC_CAL_GCB_COEFF2_FAB(X) (0x288U + (X * 0x10U)) +#define XRFDC_CAL_GCB_COEFF3_FAB(X) (0x28CU + (X * 0x10U)) +#define XRFDC_PLL_FREQ 0x300U /**< PLL output frequency (before divider) register */ +#define XRFDC_PLL_FS 0x304U /**< Sampling rate register */ +#define XRFDC_FIFO_ENABLE 0x230U /**< FIFO Enable and Disable */ +#define XRFDC_PLL_SDM_CFG0 0x00U /**< PLL Configuration bits for sdm */ +#define XRFDC_PLL_SDM_SEED0 0x18U /**< PLL Bits for sdm LSB */ +#define XRFDC_PLL_SDM_SEED1 0x1CU /**< PLL Bits for sdm MSB */ +#define XRFDC_PLL_VREG 0x44U /**< PLL bits for voltage regulator */ +#define XRFDC_PLL_VCO0 0x54U /**< PLL bits for coltage controlled oscillator LSB */ +#define XRFDC_PLL_VCO1 0x58U /**< PLL bits for coltage controlled oscillator MSB */ +#define XRFDC_PLL_CRS1 0x28U /**< PLL bits for coarse frequency control LSB */ +#define XRFDC_PLL_CRS2 0x2CU /**< PLL bits for coarse frequency control MSB */ +#define XRFDC_PLL_DIVIDER0 0x30U /**< PLL Output Divider LSB register */ +#define XRFDC_PLL_DIVIDER1 0x34U /**< PLL Output Divider MSB register */ +#define XRFDC_PLL_SPARE0 0x38U /**< PLL spare inputs LSB */ +#define XRFDC_PLL_SPARE1 0x3CU /**< PLL spare inputs MSB */ +#define XRFDC_PLL_REFDIV 0x40U /**< PLL Reference Divider register */ +#define XRFDC_PLL_VREG 0x44U /**< PLL voltage regulator */ +#define XRFDC_PLL_CHARGEPUMP 0x48U /**< PLL bits for charge pumps */ +#define XRFDC_PLL_LPF0 0x4CU /**< PLL bits for loop filters LSB */ +#define XRFDC_PLL_LPF1 0x50U /**< PLL bits for loop filters MSB */ +#define XRFDC_PLL_FPDIV 0x5CU /**< PLL Feedback Divider register */ +#define XRFDC_CLK_NETWORK_CTRL0 0x8CU /**< Clock network control and trim register */ +#define XRFDC_CLK_NETWORK_CTRL1 0x90U /**< Multi-tile sync and clock source control register */ + +#define XRFDC_HSCOM_NETWORK_CTRL1_MASK 0x02FU /**< Clock Network Register Mask for IntraTile*/ +#define XRFDC_PLL_REFDIV_MASK 0x0E0U /**< PLL Reference Divider Register Mask for IntraTile */ +#define XRFDC_PLL_DIVIDER0_ALT_MASK 0x800U /**< PLL Output Divider Register Mask for IntraTile */ + +#define XRFDC_CAL_OCB1_OFFSET_COEFF0 0x200 /**< Foreground offset correction block */ +#define XRFDC_CAL_OCB1_OFFSET_COEFF1 0x208 /**< Foreground offset correction block */ +#define XRFDC_CAL_OCB1_OFFSET_COEFF2 0x210 /**< Foreground offset correction block */ +#define XRFDC_CAL_OCB1_OFFSET_COEFF3 0x218 /**< Foreground offset correction block */ +#define XRFDC_CAL_OCB2_OFFSET_COEFF0 0x204 /**< Background offset correction block */ +#define XRFDC_CAL_OCB2_OFFSET_COEFF1 0x20C /**< Background offset correction block */ +#define XRFDC_CAL_OCB2_OFFSET_COEFF2 0x214 /**< Background offset correction block */ +#define XRFDC_CAL_OCB2_OFFSET_COEFF3 0x21C /**< Background offset correction block */ +#define XRFDC_CAL_GCB_OFFSET_COEFF0 0x220 /**< Background gain correction block */ +#define XRFDC_CAL_GCB_OFFSET_COEFF1 0x224 /**< Background gain correction block */ +#define XRFDC_CAL_GCB_OFFSET_COEFF2 0x228 /**< Background gain correction block */ +#define XRFDC_CAL_GCB_OFFSET_COEFF3 0x22C /**< Background gain correction block */ +#define XRFDC_CAL_GCB_OFFSET_COEFF0_ALT 0x220 /**< Background gain correction block (below Gen 3) */ +#define XRFDC_CAL_GCB_OFFSET_COEFF1_ALT 0x228 /**< Background gain correction block (below Gen 3) */ +#define XRFDC_CAL_GCB_OFFSET_COEFF2_ALT 0x230 /**< Background gain correction block (below Gen 3) */ +#define XRFDC_CAL_GCB_OFFSET_COEFF3_ALT 0x238 /**< Background gain correction block (below Gen 3) */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF0 0x170 /**< Background time skew correction block */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF1 0x174 /**< Background time skew correction block */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF2 0x178 /**< Background time skew correction block */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF3 0x17C /**< Background time skew correction block */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF4 0x180 /**< Background time skew correction block */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF5 0x184 /**< Background time skew correction block */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF6 0x188 /**< Background time skew correction block */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF7 0x18C /**< Background time skew correction block */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF0_ALT 0x168 /**< Background time skew correction block (below Gen 3) */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF1_ALT 0x16C /**< Background time skew correction block (below Gen 3) */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF2_ALT 0x170 /**< Background time skew correction block (below Gen 3) */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF3_ALT 0x174 /**< Background time skew correction block (below Gen 3) */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF4_ALT 0x178 /**< Background time skew correction block (below Gen 3) */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF5_ALT 0x17C /**< Background time skew correction block (below Gen 3) */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF6_ALT 0x180 /**< Background time skew correction block (below Gen 3) */ +#define XRFDC_CAL_TSCB_OFFSET_COEFF7_ALT 0x184 /**< Background time skew correction block (below Gen 3) */ + + +/* @} */ + +/** @name Calibration Coefficients - Calibration coefficients and disable registers + * + * This register contains bits for calibration coefficients + * for ADC. + * @{ + */ + +#define XRFDC_CAL_OCB_MASK 0xFFFFU /**< offsets coeff mask*/ +#define XRFDC_CAL_GCB_MASK 0x0FFFU /**< gain coeff mask*/ +#define XRFDC_CAL_GCB_FAB_MASK 0xFFF0U /**< gain coeff mask for IP Gen 2 or below*/ +#define XRFDC_CAL_TSCB_MASK 0x01FFU /**< time skew coeff mask*/ + +#define XRFDC_CAL_GCB_FLSH_MASK 0x1000U /**< GCB accumulator flush mask*/ +#define XRFDC_CAL_GCB_ACEN_MASK 0x0800U /**< GCB accumulator enable mask*/ +#define XRFDC_CAL_GCB_ENFL_MASK 0x1800U /**< GCB accumulator enable mask*/ + +#define XRFDC_CAL_OCB_EN_MASK 0x0001U /**< offsets coeff override enable mask*/ +#define XRFDC_CAL_GCB_EN_MASK 0x0080U /**< gain coeff override enable mask*/ +#define XRFDC_CAL_TSCB_EN_MASK 0x8000U /**< time skew coeff override enable mask*/ + +#define XRFDC_CAL_OCB_EN_SHIFT 0U /**< offsets coeff shift*/ +#define XRFDC_CAL_GCB_EN_SHIFT 7U /**< gain coeff shift*/ +#define XRFDC_CAL_TSCB_EN_SHIFT 15U /**< time skew coeff shift*/ +#define XRFDC_CAL_GCB_FLSH_SHIFT 12U /**< GCB accumulator flush shift*/ +#define XRFDC_CAL_GCB_ACEN_SHIFT 11U /**< GCB accumulator enable shift*/ + +#define XRFDC_CAL_SLICE_SHIFT 16U /**<Coefficient shift for HSADCs*/ + +/* @} */ +/** @name Calibration Coefficients - Calibration coefficients and disable registers + * + * This register contains bits for calibration coefficients + * for ADC. + * @{ + */ + +#define XRFDC_CAL_FREEZE_CAL_MASK 0x1U /**< Calibration freeze enable mask*/ +#define XRFDC_CAL_FREEZE_STS_MASK 0x2U /**< Calibration freeze status mask*/ +#define XRFDC_CAL_FREEZE_PIN_MASK 0x4U /**< Calibration freeze pin disable mask*/ + +#define XRFDC_CAL_FREEZE_CAL_SHIFT 0U /**< Calibration freeze enable shift*/ +#define XRFDC_CAL_FREEZE_STS_SHIFT 1U /**< Calibration freeze status shift*/ +#define XRFDC_CAL_FREEZE_PIN_SHIFT 2U /**< Calibration freeze pin disable shift*/ + +/* @} */ + +/** @name FIFO Enable - FIFO enable and disable register + * + * This register contains bits for FIFO enable and disable + * for ADC and DAC. + * @{ + */ + +#define XRFDC_FIFO_EN_MASK 0x00000001U /**< FIFO enable/disable */ +#define XRFDC_RESTART_MASK 0x00000001U /**< Restart bit mask */ + +/* @} */ + +/** @name Clock Enable - FIFO Latency, fabric, DataPath, + * full-rate, output register + * + * This register contains bits for various clock enable options of + * the ADC. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_CLK_EN_CAL_MASK 0x00000001U /**< Enable Output + Register clock */ +#define XRFDC_CLK_EN_DIG_MASK 0x00000002U /**< Enable full-rate clock */ +#define XRFDC_CLK_EN_DP_MASK 0x00000004U /**< Enable Data Path clock */ +#define XRFDC_CLK_EN_FAB_MASK 0x00000008U /**< Enable fabric clock */ +#define XRFDC_DAT_CLK_EN_MASK 0x0000000FU /**< Data Path Clk enable */ +#define XRFDC_CLK_EN_LM_MASK 0x00000010U /**< Enable for FIFO + Latency measurement clock */ + +/* @} */ + +/** @name Debug reset - FIFO Latency, fabric, DataPath, + * full-rate, output register + * + * This register contains bits for various Debug reset options of + * the ADC. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_DBG_RST_CAL_MASK 0x00000001U /**< Reset clk_cal + clock domain */ +#define XRFDC_DBG_RST_DP_MASK 0x00000002U /**< Reset data path + clock domain */ +#define XRFDC_DBG_RST_FAB_MASK 0x00000004U /**< Reset clock fabric + clock domain */ +#define XRFDC_DBG_RST_DIG_MASK 0x00000008U /**< Reset clk_dig clock + domain */ +#define XRFDC_DBG_RST_DRP_CAL_MASK 0x00000010U /**< Reset subadc-drp + register on clock cal */ +#define XRFDC_DBG_RST_LM_MASK 0x00000020U /**< Reset FIFO Latency + measurement clock domain */ + +/* @} */ + +/** @name Fabric rate - Fabric data rate for read and write + * + * This register contains bits for read and write fabric data + * rate for ADC. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_ADC_FAB_RATE_WR_MASK 0x0000000FU /**< ADC FIFO Write Number + of Words per clock */ +#define XRFDC_DAC_FAB_RATE_WR_MASK 0x0000001FU /**< DAC FIFO Write Number + of Words per clock */ +#define XRFDC_FAB_RATE_RD_MASK 0x00000F00U /**< FIFO Read Number + of words per clock */ +#define XRFDC_FAB_RATE_RD_SHIFT 8U /**< Fabric Read shift */ + +/* @} */ + +/** @name Fabric Offset - FIFO de-skew + * + * This register contains bits of Fabric Offset. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_FAB_RD_PTR_OFFST_MASK 0x0000003FU /**< FIFO read pointer + offset for interface de-skew */ + +/* @} */ + +/** @name Fabric ISR - Interrupt status register for FIFO interface + * + * This register contains bits of margin-indicator and user-data overlap + * (overflow/underflow). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_FAB_ISR_USRDAT_OVR_MASK 0x00000001U /**< User-data overlap- + data written faster than read (overflow) */ +#define XRFDC_FAB_ISR_USRDAT_UND_MASK 0x00000002U /**< User-data overlap- + data read faster than written (underflow) */ +#define XRFDC_FAB_ISR_USRDAT_MASK 0x00000003U /**< User-data overlap Mask */ +#define XRFDC_FAB_ISR_MARGIND_OVR_MASK 0x00000004U /**< Marginal-indicator + overlap (overflow) */ +#define XRFDC_FAB_ISR_MARGIND_UND_MASK 0x00000008U /**< Marginal-indicator + overlap (underflow) */ +/* @} */ + +/** @name Fabric IMR - Interrupt mask register for FIFO interface + * + * This register contains bits of margin-indicator and user-data overlap + * (overflow/underflow). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_FAB_IMR_USRDAT_OVR_MASK 0x00000001U /**< User-data overlap- + data written faster than read (overflow) */ +#define XRFDC_FAB_IMR_USRDAT_UND_MASK 0x00000002U /**< User-data overlap- + data read faster than written (underflow) */ +#define XRFDC_FAB_IMR_USRDAT_MASK 0x00000003U /**< User-data overlap Mask */ +#define XRFDC_FAB_IMR_MARGIND_OVR_MASK 0x00000004U /**< Marginal-indicator + overlap (overflow) */ +#define XRFDC_FAB_IMR_MARGIND_UND_MASK 0x00000008U /**< Marginal-indicator + overlap (underflow) */ +/* @} */ + +/** @name Update Dynamic - Trigger a dynamic update event + * + * This register contains bits of update event for slice, nco, qmc + * and coarse delay. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_UPDT_EVNT_MASK 0x0000000FU /**< Update event mask */ +#define XRFDC_UPDT_EVNT_SLICE_MASK 0x00000001U /**< Trigger a slice update + event apply to _DCONFIG reg */ +#define XRFDC_UPDT_EVNT_NCO_MASK 0x00000002U /**< Trigger a update event + apply to NCO_DCONFIG reg */ +#define XRFDC_UPDT_EVNT_QMC_MASK 0x00000004U /**< Trigger a update event + apply to QMC_DCONFIG reg */ +#define XRFDC_ADC_UPDT_CRSE_DLY_MASK 0x00000008U /**< ADC Trigger a update event + apply to Coarse delay_DCONFIG reg */ +#define XRFDC_DAC_UPDT_CRSE_DLY_MASK 0x00000020U /**< DAC Trigger a update event + apply to Coarse delay_DCONFIG reg */ +/* @} */ + +/** @name FIFO Latency control - Config registers for FIFO Latency measurement + * + * This register contains bits of FIFO Latency ctrl for disable, restart and + * set fifo latency measurement. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_FIFO_LTNCY_PRD_MASK 0x00000007U /**< Set FIFO Latency + measurement period */ +#define XRFDC_FIFO_LTNCY_RESTRT_MASK 0x00000008U /**< Restart FIFO Latency + measurement */ +#define XRFDC_FIFO_LTNCY_DIS_MASK 0x000000010U /**< Disable FIFO Latency + measurement */ + +/* @} */ + +/** @name Decode ISR - ISR for Decoder Interface + * + * This register contains bits of subadc 0,1,2 and 3 decoder overflow + * and underflow range. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_DEC_ISR_SUBADC_MASK 0x000000FFU /**< subadc decoder + Mask */ +#define XRFDC_DEC_ISR_SUBADC0_UND_MASK 0x00000001U /**< subadc0 decoder + underflow range */ +#define XRFDC_DEC_ISR_SUBADC0_OVR_MASK 0x00000002U /**< subadc0 decoder + overflow range */ +#define XRFDC_DEC_ISR_SUBADC1_UND_MASK 0x00000004U /**< subadc1 decoder + underflow range */ +#define XRFDC_DEC_ISR_SUBADC1_OVR_MASK 0x00000008U /**< subadc1 decoder + overflow range */ +#define XRFDC_DEC_ISR_SUBADC2_UND_MASK 0x00000010U /**< subadc2 decoder + underflow range */ +#define XRFDC_DEC_ISR_SUBADC2_OVR_MASK 0x00000020U /**< subadc2 decoder + overflow range */ +#define XRFDC_DEC_ISR_SUBADC3_UND_MASK 0x00000040U /**< subadc3 decoder + underflow range */ +#define XRFDC_DEC_ISR_SUBADC3_OVR_MASK 0x00000080U /**< subadc3 decoder + overflow range */ + +/* @} */ + +/** @name Decode IMR - IMR for Decoder Interface + * + * This register contains bits of subadc 0,1,2 and 3 decoder overflow + * and underflow range. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_DEC_IMR_SUBADC0_UND_MASK 0x00000001U /**< subadc0 decoder + underflow range */ +#define XRFDC_DEC_IMR_SUBADC0_OVR_MASK 0x00000002U /**< subadc0 decoder + overflow range */ +#define XRFDC_DEC_IMR_SUBADC1_UND_MASK 0x00000004U /**< subadc1 decoder + underflow range */ +#define XRFDC_DEC_IMR_SUBADC1_OVR_MASK 0x00000008U /**< subadc1 decoder + overflow range */ +#define XRFDC_DEC_IMR_SUBADC2_UND_MASK 0x00000010U /**< subadc2 decoder + underflow range */ +#define XRFDC_DEC_IMR_SUBADC2_OVR_MASK 0x00000020U /**< subadc2 decoder + overflow range */ +#define XRFDC_DEC_IMR_SUBADC3_UND_MASK 0x00000040U /**< subadc3 decoder + underflow range */ +#define XRFDC_DEC_IMR_SUBADC3_OVR_MASK 0x00000080U /**< subadc3 decoder + overflow range */ +#define XRFDC_DEC_IMR_MASK 0x000000FFU + +/* @} */ + +/** @name DataPath (DAC)- FIFO Latency, Image Reject Filter, Mode, + * + * This register contains bits for DataPath latency, Image Reject Filter + * and the Mode for the DAC. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_DATAPATH_MODE_MASK 0x00000003U /**< DataPath Mode */ +#define XRFDC_DATAPATH_IMR_MASK 0x00000004U /**< IMR Mode */ +#define XRFDC_DATAPATH_LATENCY_MASK 0x00000008U /**< DataPath Latency */ + +/* @} */ + + + +/** @name DataPath ISR - ISR for Data Path interface + * + * This register contains bits of QMC Gain/Phase overflow, offset overflow, + * Decimation I-Path and Interpolation Q-Path overflow for stages 0,1,2. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_ADC_DAT_PATH_ISR_MASK 0x000000FFU /**< ADC Data Path Overflow */ +#define XRFDC_DAC_DAT_PATH_ISR_MASK 0x000001FFU /**< DAC Data Path Overflow */ +#define XRFDC_DAT_ISR_DECI_IPATH_MASK 0x00000007U /**< Decimation I-Path + overflow for stages 0,1,2 */ +#define XRFDC_DAT_ISR_INTR_QPATH_MASK 0x00000038U /**< Interpolation + Q-Path overflow for stages 0,1,2 */ +#define XRFDC_DAT_ISR_QMC_GAIN_MASK 0x00000040U /**< QMC Gain/Phase + overflow */ +#define XRFDC_DAT_ISR_QMC_OFFST_MASK 0x00000080U /**< QMC offset + overflow */ +#define XRFDC_DAC_DAT_ISR_INVSINC_MASK 0x00000100U /**< Inverse Sinc offset + overflow */ + +/* @} */ + +/** @name DataPath IMR - IMR for Data Path interface + * + * This register contains bits of QMC Gain/Phase overflow, offset overflow, + * Decimation I-Path and Interpolation Q-Path overflow for stages 0,1,2. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_DAT_IMR_DECI_IPATH_MASK 0x00000007U /**< Decimation I-Path + overflow for stages 0,1,2 */ +#define XRFDC_DAT_IMR_INTR_QPATH_MASK 0x00000038U /**< Interpolation + Q-Path overflow for stages 0,1,2 */ +#define XRFDC_DAT_IMR_QMC_GAIN_MASK 0x00000040U /**< QMC Gain/Phase + overflow */ +#define XRFDC_DAT_IMR_QMC_OFFST_MASK 0x00000080U /**< QMC offset + overflow */ +#define XRFDC_ADC_DAT_IMR_MASK 0x000000FFU /**< ADC DataPath mask */ +#define XRFDC_DAC_DAT_IMR_MASK 0x00000FFFU /**< DAC DataPath mask */ + +/* @} */ + +/** @name Decimation Config - Decimation control + * + * This register contains bits to configure the decimation in terms of + * the type of data. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_DEC_CFG_MASK 0x00000003U /**< ChannelA (2GSPS real + data from Mixer I output) */ +#define XRFDC_DEC_CFG_CHA_MASK 0x00000000U /**< ChannelA(I) */ +#define XRFDC_DEC_CFG_CHB_MASK 0x00000001U /**< ChannelB (2GSPS real + data from Mixer Q output) */ +#define XRFDC_DEC_CFG_IQ_MASK 0x00000002U /**< IQ-2GSPS */ +#define XRFDC_DEC_CFG_4GSPS_MASK 0x00000003U /**< 4GSPS may be I or Q + or Real depending on high level block config */ + +/* @} */ + +/** @name Decimation Mode - Decimation Rate + * + * This register contains bits to configures the decimation rate. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_DEC_MOD_MASK 0x00000007U /**< Decimation mode Mask */ +#define XRFDC_DEC_MOD_MASK_EXT 0x0000003FU /**< Decimation mode Mask */ + +/* @} */ + +/** @name Mixer config0 - Configure I channel coarse mixer mode of operation + * + * This register contains bits to set the output data sequence of + * I channel. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_MIX_CFG0_MASK 0x00000FFFU /**< Mixer Config0 Mask */ +#define XRFDC_MIX_I_DAT_WRD0_MASK 0x00000007U /**< Output data word[0] + of I channel */ +#define XRFDC_MIX_I_DAT_WRD1_MASK 0x00000038U /**< Output data word[1] + of I channel */ +#define XRFDC_MIX_I_DAT_WRD2_MASK 0x000001C0U /**< Output data word[2] + of I channel */ +#define XRFDC_MIX_I_DAT_WRD3_MASK 0x00000E00U /**< Output data word[3] + of I channel */ + +/* @} */ + +/** @name Mixer config1 - Configure Q channel coarse mixer mode of operation + * + * This register contains bits to set the output data sequence of + * Q channel. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_MIX_CFG1_MASK 0x00000FFFU /**< Mixer Config0 Mask */ +#define XRFDC_MIX_Q_DAT_WRD0_MASK 0x00000007U /**< Output data word[0] + of Q channel */ +#define XRFDC_MIX_Q_DAT_WRD1_MASK 0x00000038U /**< Output data word[1] + of Q channel */ +#define XRFDC_MIX_Q_DAT_WRD2_MASK 0x000001C0U /**< Output data word[2] + of Q channel */ +#define XRFDC_MIX_Q_DAT_WRD3_MASK 0x00000E00U /**< Output data word[3] + of Q channel */ + +/* @} */ + +/** @name Mixer mode - Configure mixer mode of operation + * + * This register contains bits to set NCO phases, NCO output scale + * and fine mixer multipliers. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_EN_I_IQ_MASK 0x00000003U /**< Enable fine mixer + multipliers on IQ i/p for I output */ +#define XRFDC_EN_Q_IQ_MASK 0x0000000CU /**< Enable fine mixer + multipliers on IQ i/p for Q output */ +#define XRFDC_FINE_MIX_SCALE_MASK 0x00000010U /**< NCO output scale */ +#define XRFDC_SEL_I_IQ_MASK 0x00000F00U /**< Select NCO phases + for I output */ +#define XRFDC_SEL_Q_IQ_MASK 0x0000F000U /**< Select NCO phases + for Q output */ +#define XRFDC_I_IQ_COS_MINSIN 0x00000C00U /**< Select NCO phases + for I output */ +#define XRFDC_Q_IQ_SIN_COS 0x00001000U /**< Select NCO phases + for Q output */ +#define XRFDC_MIXER_MODE_C2C_MASK 0x0000000FU /**< Mixer mode C2C Mask */ +#define XRFDC_MIXER_MODE_R2C_MASK 0x00000005U /**< Mixer mode R2C Mask */ +#define XRFDC_MIXER_MODE_C2R_MASK 0x00000003U /**< Mixer mode C2R Mask */ +#define XRFDC_MIXER_MODE_OFF_MASK 0x00000000U /**< Mixer mode OFF Mask */ +/* @} */ + +/** @name NCO update - NCO update mode + * + * This register contains bits to Select event source, delay and reset delay. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_NCO_UPDT_MODE_MASK 0x00000007U /**< NCO event source + selection mask */ +#define XRFDC_NCO_UPDT_MODE_GRP 0x00000000U /**< NCO event source + selection is Group */ +#define XRFDC_NCO_UPDT_MODE_SLICE 0x00000001U /**< NCO event source + selection is slice */ +#define XRFDC_NCO_UPDT_MODE_TILE 0x00000002U /**< NCO event source + selection is tile */ +#define XRFDC_NCO_UPDT_MODE_SYSREF 0x00000003U /**< NCO event source + selection is Sysref */ +#define XRFDC_NCO_UPDT_MODE_MARKER 0x00000004U /**< NCO event source + selection is Marker */ +#define XRFDC_NCO_UPDT_MODE_FABRIC 0x00000005U /**< NCO event source + selection is fabric */ +#define XRFDC_NCO_UPDT_DLY_MASK 0x00001FF8U /**< delay in clk_dp + cycles in application of event after arrival */ +#define XRFDC_NCO_UPDT_RST_DLY_MASK 0x0000D000U /**< optional delay on + the NCO phase reset delay */ + +/* @} */ + +/** @name NCO Phase Reset - NCO Slice Phase Reset + * + * This register contains bits to reset the nco phase of the current + * slice phase accumulator. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_NCO_PHASE_RST_MASK 0x00000001U /**< Reset NCO Phase + of current slice */ + +/* @} */ + +/** @name DAC interpolation data + * + * This register contains bits for DAC interpolation data type + * @{ + */ + +#define XRFDC_DAC_INTERP_DATA_MASK 0x00000001U /**< Data type mask */ + +/* @} */ + +/** @name NCO Freq Word[47:32] - NCO Phase increment(nco freq 48-bit) + * + * This register contains bits for frequency control word of the + * NCO. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_NCO_FQWD_UPP_MASK 0x0000FFFFU /**< NCO Phase + increment[47:32] */ +#define XRFDC_NCO_FQWD_UPP_SHIFT 32U /**< Freq Word upper shift */ + +/* @} */ + +/** @name NCO Freq Word[31:16] - NCO Phase increment(nco freq 48-bit) + * + * This register contains bits for frequency control word of the + * NCO. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_NCO_FQWD_MID_MASK 0x0000FFFFU /**< NCO Phase + increment[31:16] */ +#define XRFDC_NCO_FQWD_MID_SHIFT 16U /**< Freq Word Mid shift */ + +/* @} */ + +/** @name NCO Freq Word[15:0] - NCO Phase increment(nco freq 48-bit) + * + * This register contains bits for frequency control word of the + * NCO. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_NCO_FQWD_LOW_MASK 0x0000FFFFU /**< NCO Phase + increment[15:0] */ +#define XRFDC_NCO_FQWD_MASK 0x0000FFFFFFFFFFFFU /**< NCO Freq + offset[48:0] */ + +/* @} */ + +/** @name NCO Phase Offset[17:16] - NCO Phase offset + * + * This register contains bits to set NCO Phase offset(18-bit offset + * added to the phase accumulator). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_NCO_PHASE_UPP_MASK 0x00000003U /**< NCO Phase + offset[17:16] */ +#define XRFDC_NCO_PHASE_UPP_SHIFT 16U /**< NCO phase upper shift */ + +/* @} */ + +/** @name NCO Phase Offset[15:0] - NCO Phase offset + * + * This register contains bits to set NCO Phase offset(18-bit offset + * added to the phase accumulator). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_NCO_PHASE_LOW_MASK 0x0000FFFFU /**< NCO Phase + offset[15:0] */ +#define XRFDC_NCO_PHASE_MASK 0x0003FFFFU /**< NCO Phase + offset[17:0] */ + +/* @} */ + +/** @name NCO Phase mode - NCO Control setting mode + * + * This register contains bits to set NCO mode of operation. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_NCO_PHASE_MOD_MASK 0x00000003U /**< NCO mode + of operation mask */ +#define XRFDC_NCO_PHASE_MOD_4PHASE 0x00000003U /**< NCO output + 4 successive phase */ +#define XRFDC_NCO_PHASE_MOD_EVEN 0x00000001U /**< NCO output + even phase */ +#define XRFDC_NCO_PHASE_MODE_ODD 0x00000002U /**< NCO output + odd phase */ +/* @} */ + +/** @name QMC update - QMC update mode + * + * This register contains bits to Select event source and delay. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_QMC_UPDT_MODE_MASK 0x00000007U /**< QMC event source + selection mask */ +#define XRFDC_QMC_UPDT_MODE_GRP 0x00000000U /**< QMC event source + selection is group */ +#define XRFDC_QMC_UPDT_MODE_SLICE 0x00000001U /**< QMC event source + selection is slice */ +#define XRFDC_QMC_UPDT_MODE_TILE 0x00000002U /**< QMC event source + selection is tile */ +#define XRFDC_QMC_UPDT_MODE_SYSREF 0x00000003U /**< QMC event source + selection is Sysref */ +#define XRFDC_QMC_UPDT_MODE_MARKER 0x00000004U /**< QMC event source + selection is Marker */ +#define XRFDC_QMC_UPDT_MODE_FABRIC 0x00000005U /**< QMC event source + selection is fabric */ +#define XRFDC_QMC_UPDT_DLY_MASK 0x00001FF8U /**< delay in clk_dp + cycles in application of event after arrival */ + +/* @} */ + +/** @name QMC Config - QMC Config register + * + * This register contains bits to enable QMC gain and QMC + * Phase correction. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_QMC_CFG_EN_GAIN_MASK 0x00000001U /**< enable QMC gain + correction mask */ +#define XRFDC_QMC_CFG_EN_PHASE_MASK 0x00000002U /**< enable QMC Phase + correction mask */ +#define XRFDC_QMC_CFG_PHASE_SHIFT 1U /**< QMC config phase shift */ + +/* @} */ + +/** @name QMC Offset - QMC offset correction + * + * This register contains bits to set QMC offset correction + * factor. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_QMC_OFFST_CRCTN_MASK 0x00000FFFU /**< QMC offset + correction factor */ + +/* @} */ + +/** @name QMC Gain - QMC Gain correction + * + * This register contains bits to set QMC gain correction + * factor. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_QMC_GAIN_CRCTN_MASK 0x00003FFFU /**< QMC gain + correction factor */ + +/* @} */ + +/** @name QMC Phase - QMC Phase correction + * + * This register contains bits to set QMC phase correction + * factor. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_QMC_PHASE_CRCTN_MASK 0x00000FFFU /**< QMC phase + correction factor */ + +/* @} */ + +/** @name Coarse Delay Update - Coarse delay update mode. + * + * This register contains bits to Select event source and delay. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_CRSEDLY_UPDT_MODE_MASK 0x00000007U /**< Coarse delay event + source selection mask */ +#define XRFDC_CRSEDLY_UPDT_MODE_GRP 0x00000000U /**< Coarse delay event + source selection is group */ +#define XRFDC_CRSEDLY_UPDT_MODE_SLICE 0x00000001U/**< Coarse delay event + source selection is slice */ +#define XRFDC_CRSEDLY_UPDT_MODE_TILE 0x00000002U /**< Coarse delay event + source selection is tile */ +#define XRFDC_CRSEDLY_UPDT_MODE_SYSREF 0x00000003U /**< Coarse delay event + source selection is sysref */ +#define XRFDC_CRSEDLY_UPDT_MODE_MARKER 0x00000004U /**< Coarse delay event + source selection is Marker */ +#define XRFDC_CRSEDLY_UPDT_MODE_FABRIC 0x00000005U /**< Coarse delay event + source selection is fabric */ +#define XRFDC_CRSEDLY_UPDT_DLY_MASK 0x00001FF8U /**< delay in clk_dp + cycles in application of event after arrival */ + +/* @} */ + +/** @name Coarse delay Config - Coarse delay select + * + * This register contains bits to select coarse delay. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_CRSE_DLY_CFG_MASK 0x00000007U /**< Coarse delay select */ +#define XRFDC_CRSE_DLY_CFG_MASK_EXT 0x0000003FU /**< Extended coarse delay select*/ + +/* @} */ + +/** @name Data Scaling Config - Data Scaling enable + * + * This register contains bits to enable data scaling. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_DAT_SCALE_CFG_MASK 0x00000001U /**< Enable data scaling */ + +/* @} */ + +/** @name Data Scaling Config - Data Scaling enable + * + * This register contains bits to enable data scaling. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_DAT_SCALE_CFG_MASK 0x00000001U /**< Enable data scaling */ + +/* @} */ + +/** @name Switch Matrix Config + * + * This register contains bits to control crossbar switch that select + * data to mixer block. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_SWITCH_MTRX_MASK 0x0000003FU /**< Switch matrix mask */ +#define XRFDC_SEL_CB_TO_MIX1_MASK 0x00000003U /**< Control crossbar + switch that select the data to mixer block mux1 */ +#define XRFDC_SEL_CB_TO_MIX0_MASK 0x0000000CU /**< Control crossbar + switch that select the data to mixer block mux0 */ +#define XRFDC_SEL_CB_TO_QMC_MASK 0x00000010U /**< Control crossbar + switch that select the data to QMC */ +#define XRFDC_SEL_CB_TO_DECI_MASK 0x00000020U /**< Control crossbar + switch that select the data to decimation filter */ +#define XRFDC_SEL_CB_TO_MIX0_SHIFT 2U /**< Crossbar Mixer0 shift */ + +/* @} */ + +/** @name Threshold0 Config + * + * This register contains bits to select mode, clear mode and to + * clear sticky bit. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD0_EN_MOD_MASK 0x00000003U /**< Enable Threshold0 + block */ +#define XRFDC_TRSHD0_CLR_MOD_MASK 0x00000004U /**< Clear mode */ +#define XRFDC_TRSHD0_STIKY_CLR_MASK 0x00000008U /**< Clear sticky bit */ + +/* @} */ + +/** @name Threshold0 Average[31:16] + * + * This register contains bits to select Threshold0 under averaging. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD0_AVG_UPP_MASK 0x0000FFFFU /**< Threshold0 under + Averaging[31:16] */ +#define XRFDC_TRSHD0_AVG_UPP_SHIFT 16U /**< Threshold0 Avg upper shift */ +/* @} */ + +/** @name Threshold0 Average[15:0] + * + * This register contains bits to select Threshold0 under averaging. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD0_AVG_LOW_MASK 0x0000FFFFU /**< Threshold0 under + Averaging[15:0] */ + +/* @} */ + +/** @name Threshold0 Under threshold + * + * This register contains bits to select Threshold0 under threshold. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD0_UNDER_MASK 0x00007FFFU /**< Threshold0 under + Threshold[14:0] */ + +/* @} */ + +/** @name Threshold0 Over threshold + * + * This register contains bits to select Threshold0 over threshold. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD0_OVER_MASK 0x00007FFFU /**< Threshold0 under + Threshold[14:0] */ + +/* @} */ + +/** @name Threshold1 Config + * + * This register contains bits to select mode, clear mode and to + * clear sticky bit. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD1_EN_MOD_MASK 0x00000003U /**< Enable Threshold1 + block */ +#define XRFDC_TRSHD1_CLR_MOD_MASK 0x00000004U /**< Clear mode */ +#define XRFDC_TRSHD1_STIKY_CLR_MASK 0x00000008U /**< Clear sticky bit */ + +/* @} */ + +/** @name Threshold1 Average[31:16] + * + * This register contains bits to select Threshold1 under averaging. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD1_AVG_UPP_MASK 0x0000FFFFU /**< Threshold1 under + Averaging[31:16] */ +#define XRFDC_TRSHD1_AVG_UPP_SHIFT 16U /**< Threshold1 Avg upper shift */ + +/* @} */ + +/** @name Threshold1 Average[15:0] + * + * This register contains bits to select Threshold1 under averaging. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD1_AVG_LOW_MASK 0x0000FFFFU /**< Threshold1 under + Averaging[15:0] */ + +/* @} */ + +/** @name Threshold1 Under threshold + * + * This register contains bits to select Threshold1 under threshold. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD1_UNDER_MASK 0x00007FFFU /**< Threshold1 under + Threshold[14:0] */ + +/* @} */ + +/** @name Threshold1 Over threshold + * + * This register contains bits to select Threshold1 over threshold. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TRSHD1_OVER_MASK 0x00007FFFU /**< Threshold1 under + Threshold[14:0] */ + +/* @} */ + +/** @name FrontEnd Data Control + * + * This register contains bits to select raw data and cal coefficient to + * be streamed to memory. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_FEND_DAT_CTRL_MASK 0x000000FFU /**< raw data and cal + coefficient to be streamed to memory */ + +/* @} */ + +/** @name TI Digital Correction Block control0 + * + * This register contains bits for Time Interleaved digital correction + * block gain and offset correction. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_CTRL0_MASK 0x0000FFFFU /**< TI DCB gain and + offset correction */ +#define XRFDC_TI_DCB_MODE_MASK 0x00007800U /**< TI DCB Mode mask */ + +/* @} */ + +/** @name TI Digital Correction Block control1 + * + * This register contains bits for Time Interleaved digital correction + * block gain and offset correction. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_CTRL1_MASK 0x00001FFFU /**< TI DCB gain and + offset correction */ + +/* @} */ + +/** @name TI Digital Correction Block control2 + * + * This register contains bits for Time Interleaved digital correction + * block gain and offset correction. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_CTRL2_MASK 0x00001FFFU /**< TI DCB gain and + offset correction */ + +/* @} */ + +/** @name TI Time Skew control0 + * + * This register contains bits for Time skew correction control bits0(enables, + * mode, multiplier factors, debug). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_TISK_EN_MASK 0x00000001U /**< Block Enable */ +#define XRFDC_TI_TISK_MODE_MASK 0x00000002U /**< Mode (2G/4G) */ +#define XRFDC_TI_TISK_ZONE_MASK 0x00000004U /**< Specifies Nyquist zone */ +#define XRFDC_TI_TISK_CHOP_EN_MASK 0x00000008U /**< enable chopping mode */ +#define XRFDC_TI_TISK_MU_CM_MASK 0x000000F0U /**< Constant mu_cm multiplying + common mode path */ +#define XRFDC_TI_TISK_MU_DF_MASK 0x00000F00U /**< Constant mu_df multiplying + differential path */ +#define XRFDC_TI_TISK_DBG_CTRL_MASK 0x0000F000U /**< Debug control */ +#define XRFDC_TI_TISK_DBG_UPDT_RT_MASK 0x00001000U /**< Debug update rate */ +#define XRFDC_TI_TISK_DITH_DLY_MASK 0x0000E000U /**< Programmable delay on + dither path to match data path */ +#define XRFDC_TISK_ZONE_SHIFT 2U /**< Nyquist zone shift */ + +/* @} */ + +/** @name DAC MC Config0 + * + * This register contains bits for enable/disable shadow logic , Nyquist zone + * selction, enable full speed clock, Programmable delay. + * @{ + */ + +#define XRFDC_MC_CFG0_MIX_MODE_MASK 0x00000002U /**< Enable + Mixing mode */ +#define XRFDC_MC_CFG0_MIX_MODE_SHIFT 1U /**< Mix mode shift */ + +/* @} */ + +/** @name TI Time Skew control0 + * + * This register contains bits for Time skew correction control bits0(enables, + * mode, multiplier factors, debug). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_EN_MASK 0x00000001U /**< Block Enable */ +#define XRFDC_TISK_MODE_MASK 0x00000002U /**< Mode (2G/4G) */ +#define XRFDC_TISK_ZONE_MASK 0x00000004U /**< Specifies Nyquist zone */ +#define XRFDC_TISK_CHOP_EN_MASK 0x00000008U /**< enable chopping mode */ +#define XRFDC_TISK_MU_CM_MASK 0x000000F0U /**< Constant mu_cm multiplying + common mode path */ +#define XRFDC_TISK_MU_DF_MASK 0x00000F00U /**< Constant mu_df multiplying + differential path */ +#define XRFDC_TISK_DBG_CTRL_MASK 0x0000F000U /**< Debug control */ +#define XRFDC_TISK_DBG_UPDT_RT_MASK 0x00001000U /**< Debug update rate */ +#define XRFDC_TISK_DITH_DLY_MASK 0x0000E000U /**< Programmable delay on + dither path to match data path */ + +/* @} */ + +/** @name TI Time Skew control1 + * + * This register contains bits for Time skew correction control bits1 + * (Deadzone Parameters). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_DZ_MIN_VAL_MASK 0x000000FFU /**< Deadzone min */ +#define XRFDC_TISK_DZ_MAX_VAL_MASK 0x0000FF00U /**< Deadzone max */ + +/* @} */ + +/** @name TI Time Skew control2 + * + * This register contains bits for Time skew correction control bits2 + * (Filter parameters). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_MU0_MASK 0x0000000FU /**< Filter0 multiplying factor */ +#define XRFDC_TISK_BYPASS0_MASK 0x00000080U /**< ByPass filter0 */ +#define XRFDC_TISK_MU1_MASK 0x00000F00U /**< Filter1 multiplying factor */ +#define XRFDC_TISK_BYPASS1_MASK 0x00008000U /**< Filter1 multiplying factor */ + +/* @} */ + +/** @name TI Time Skew control3 + * + * This register contains bits for Time skew control settling time + * following code update. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_SETTLE_MASK 0x000000FFU /**< Settling time following + code update */ + +/* @} */ + +/** @name TI Time Skew control4 + * + * This register contains bits for Time skew control setting time + * following code update. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_CAL_PRI_MASK 0x00000001U /**< */ +#define XRFDC_TISK_DITH_INV_MASK 0x00000FF0U /**< */ + +/* @} */ + +/** @name TI Time Skew DAC0 + * + * This register contains bits for Time skew DAC cal code of + * subadc ch0. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_DAC0_CODE_MASK 0x000000FFU /**< Code to correction + DAC of subadc ch0 front end switch0 */ +#define XRFDC_TISK_DAC0_OVRID_EN_MASK 0x00008000U /**< override enable */ + +/* @} */ + +/** @name TI Time Skew DAC1 + * + * This register contains bits for Time skew DAC cal code of + * subadc ch1. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_DAC1_CODE_MASK 0x000000FFU /**< Code to correction + DAC of subadc ch1 front end switch0 */ +#define XRFDC_TISK_DAC1_OVRID_EN_MASK 0x00008000U /**< override enable */ + +/* @} */ + +/** @name TI Time Skew DAC2 + * + * This register contains bits for Time skew DAC cal code of + * subadc ch2. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_DAC2_CODE_MASK 0x000000FFU /**< Code to correction + DAC of subadc ch2 front end switch0 */ +#define XRFDC_TISK_DAC2_OVRID_EN_MASK 0x00008000U /**< override enable */ + +/* @} */ + +/** @name TI Time Skew DAC3 + * + * This register contains bits for Time skew DAC cal code of + * subadc ch3. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_DAC3_CODE_MASK 0x000000FFU /**< Code to correction + DAC of subadc ch3 front end switch0 */ +#define XRFDC_TISK_DAC3_OVRID_EN_MASK 0x00008000U /**< override enable */ + +/* @} */ + +/** @name TI Time Skew DACP0 + * + * This register contains bits for Time skew DAC cal code of + * subadc ch0. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_DACP0_CODE_MASK 0x000000FFU /**< Code to correction + DAC of subadc ch0 front end switch1 */ +#define XRFDC_TISK_DACP0_OVRID_EN_MASK 0x00008000U /**< override enable */ + +/* @} */ + +/** @name TI Time Skew DACP1 + * + * This register contains bits for Time skew DAC cal code of + * subadc ch1. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_DACP1_CODE_MASK 0x000000FFU /**< Code to correction + DAC of subadc ch1 front end switch1 */ +#define XRFDC_TISK_DACP1_OVRID_EN_MASK 0x00008000U /**< override enable */ + +/* @} */ + +/** @name TI Time Skew DACP2 + * + * This register contains bits for Time skew DAC cal code of + * subadc ch2. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_DACP2_CODE_MASK 0x000000FFU /**< Code to correction + DAC of subadc ch2 front end switch1 */ +#define XRFDC_TISK_DACP2_OVRID_EN_MASK 0x00008000U /**< override enable */ + +/* @} */ + +/** @name TI Time Skew DACP3 + * + * This register contains bits for Time skew DAC cal code of + * subadc ch3. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TISK_DACP3_CODE_MASK 0x000000FFU /**< Code to correction + DAC of subadc ch3 front end switch1 */ +#define XRFDC_TISK_DACP3_OVRID_EN_MASK 0x00008000U /**< override enable */ + +/* @} */ + +/** @name SubDRP ADC0 address + * + * This register contains the sub-drp address of the target register. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_SUBDRP_ADC0_ADDR_MASK 0x000000FFU /**< sub-drp0 address */ + +/* @} */ + +/** @name SubDRP ADC0 Data + * + * This register contains the sub-drp data of the target register. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_SUBDRP_ADC0_DAT_MASK 0x0000FFFFU /**< sub-drp0 data + for read or write transaction */ + +/* @} */ + +/** @name SubDRP ADC1 address + * + * This register contains the sub-drp address of the target register. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_SUBDRP_ADC1_ADDR_MASK 0x000000FFU /**< sub-drp1 address */ + +/* @} */ + +/** @name SubDRP ADC1 Data + * + * This register contains the sub-drp data of the target register. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_SUBDRP_ADC1_DAT_MASK 0x0000FFFFU /**< sub-drp1 data + for read or write transaction */ + +/* @} */ + +/** @name SubDRP ADC2 address + * + * This register contains the sub-drp address of the target register. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_SUBDRP_ADC2_ADDR_MASK 0x000000FFU /**< sub-drp2 address */ + +/* @} */ + +/** @name SubDRP ADC2 Data + * + * This register contains the sub-drp data of the target register. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_SUBDRP_ADC2_DAT_MASK 0x0000FFFFU /**< sub-drp2 data + for read or write transaction */ + +/* @} */ + +/** @name SubDRP ADC3 address + * + * This register contains the sub-drp address of the target register. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_SUBDRP_ADC3_ADDR_MASK 0x000000FFU /**< sub-drp3 address */ + +/* @} */ + +/** @name SubDRP ADC3 Data + * + * This register contains the sub-drp data of the target register. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_SUBDRP_ADC3_DAT_MASK 0x0000FFFFU /**< sub-drp3 data + for read or write transaction */ + +/* @} */ + +/** @name RX MC PWRDWN + * + * This register contains the static configuration bits of ADC(RX) analog. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_RX_MC_PWRDWN_MASK 0x0000FFFFU /**< RX MC power down */ + +/* @} */ + +/** @name RX MC Config0 + * + * This register contains the static configuration bits of ADC(RX) analog. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_RX_MC_CFG0_MASK 0x0000FFFFU /**< RX MC config0 */ +#define XRFDC_RX_MC_CFG0_CM_MASK 0x00000040U /**< Coupling mode mask */ +#define XRFDC_RX_MC_CFG0_IM3_DITH_MASK 0x00000020U /**< IM3 Dither Enable mode mask */ +#define XRFDC_RX_MC_CFG0_IM3_DITH_SHIFT 5U /**< IM3 Dither Enable mode shift */ + +/* @} */ + +/** @name RX MC Config1 + * + * This register contains the static configuration bits of ADC(RX) analog. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_RX_MC_CFG1_MASK 0x0000FFFFU /**< RX MC Config1 */ + +/* @} */ + +/** @name RX MC Config2 + * + * This register contains the static configuration bits of ADC(RX) analog. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_RX_MC_CFG2_MASK 0x0000FFFFU /**< RX MC Config2 */ + +/* @} */ + +/** @name RX Pair MC Config0 + * + * This register contains the RX Pair (RX0 and RX1 or RX2 and RX3)static + * configuration bits of ADC(RX) analog. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_RX_PR_MC_CFG0_MASK 0x0000FFFFU /**< RX Pair MC Config0 */ + +/* @} */ + +/** @name RX Pair MC Config1 + * + * This register contains the RX Pair (RX0 and RX1 or RX2 and RX3)static + * configuration bits of ADC(RX) analog. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_RX_PR_MC_CFG1_MASK 0x0000FFFFU /**< RX Pair MC Config1 */ + +/* @} */ + +/** @name TI DCB Status0 BG + * + * This register contains the subadc ch0 ocb1 BG offset correction factor + * value. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS0_BG_MASK 0x0000FFFFU /**< DCB Status0 BG */ + +/* @} */ + +/** @name TI DCB Status0 FG + * + * This register contains the subadc ch0 ocb2 FG offset correction factor + * value(read and write). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS0_FG_MASK 0x0000FFFFU /**< DCB Status0 FG */ + +/* @} */ + +/** @name TI DCB Status1 BG + * + * This register contains the subadc ch1 ocb1 BG offset correction factor + * value. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS1_BG_MASK 0x0000FFFFU /**< DCB Status1 BG */ + +/* @} */ + +/** @name TI DCB Status1 FG + * + * This register contains the subadc ch1 ocb2 FG offset correction factor + * value(read and write). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS1_FG_MASK 0x0000FFFFU /**< DCB Status1 FG */ + +/* @} */ + +/** @name TI DCB Status2 BG + * + * This register contains the subadc ch2 ocb1 BG offset correction factor + * value. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS2_BG_MASK 0x0000FFFFU /**< DCB Status2 BG */ + +/* @} */ + +/** @name TI DCB Status2 FG + * + * This register contains the subadc ch2 ocb2 FG offset correction factor + * value(read and write). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS2_FG_MASK 0x0000FFFFU /**< DCB Status2 FG */ + +/* @} */ + +/** @name TI DCB Status3 BG + * + * This register contains the subadc ch3 ocb1 BG offset correction factor + * value. Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS3_BG_MASK 0x0000FFFFU /**< DCB Status3 BG */ + +/* @} */ + +/** @name TI DCB Status3 FG + * + * This register contains the subadc ch3 ocb2 FG offset correction factor + * value(read and write). Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS3_FG_MASK 0x0000FFFFU /**< DCB Status3 FG */ + +/* @} */ + +/** @name TI DCB Status4 MSB + * + * This register contains the DCB status. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS4_MSB_MASK 0x0000FFFFU /**< read the status of + gcb acc0 msb bits(subadc chan0) */ + +/* @} */ + +/** @name TI DCB Status4 LSB + * + * This register contains the DCB Status. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS4_LSB_MASK 0x0000FFFFU /**< read the status of + gcb acc0 lsb bits(subadc chan0) */ + +/* @} */ + +/** @name TI DCB Status5 MSB + * + * This register contains the DCB status. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS5_MSB_MASK 0x0000FFFFU /**< read the status of + gcb acc1 msb bits(subadc chan1) */ + +/* @} */ + +/** @name TI DCB Status5 LSB + * + * This register contains the DCB Status. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS5_LSB_MASK 0x0000FFFFU /**< read the status of + gcb acc1 lsb bits(subadc chan1) */ + +/* @} */ + +/** @name TI DCB Status6 MSB + * + * This register contains the DCB status. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS6_MSB_MASK 0x0000FFFFU /**< read the status of + gcb acc2 msb bits(subadc chan2) */ + +/* @} */ + +/** @name TI DCB Status6 LSB + * + * This register contains the DCB Status. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS6_LSB_MASK 0x0000FFFFU /**< read the status of + gcb acc2 lsb bits(subadc chan2) */ + +/* @} */ + +/** @name TI DCB Status7 MSB + * + * This register contains the DCB status. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS7_MSB_MASK 0x0000FFFFU /**< read the status of + gcb acc3 msb bits(subadc chan3) */ + +/* @} */ + +/** @name TI DCB Status7 LSB + * + * This register contains the DCB Status. Read/Write apart from the + * reserved bits. + * @{ + */ + +#define XRFDC_TI_DCB_STS7_LSB_MASK 0x0000FFFFU /**< read the status of + gcb acc3 lsb bits(subadc chan3) */ + +/* @} */ + +/** @name PLL_REFDIV + * + * This register contains the bits for Reference Clock Divider + * @{ + */ + +#define XRFDC_REFCLK_DIV_MASK 0x1FU +#define XRFDC_REFCLK_DIV_1_MASK 0x10U /**< Mask for Div1 */ +#define XRFDC_REFCLK_DIV_2_MASK 0x0U /**< Mask for Div2 */ +#define XRFDC_REFCLK_DIV_3_MASK 0x1U /**< Mask for Div3 */ +#define XRFDC_REFCLK_DIV_4_MASK 0x2U /**< Mask for Div4 */ + +/* @} */ + +/** @name FIFO Latency + * + * This register contains bits for result, key and done flag. + * Read/Write apart from the reserved bits. + * @{ + */ + +#define XRFDC_FIFO_LTNCY_RES_MASK 0x00000FFFU /**< Latency + measurement result */ +#define XRFDC_FIFO_LTNCY_KEY_MASK 0x00004000U /**< Latency + measurement result identification key */ +#define XRFDC_FIFO_LTNCY_DONE_MASK 0x00008000U /**< Latency + measurement done flag */ + +/* @} */ + +/** @name Decoder Control + * + * This register contains Unary Decoder/Randomizer settings to use. + * @{ + */ + +#define XRFDC_DEC_CTRL_MODE_MASK 0x00000007U /**< Decoder mode */ + +/* @} */ + +/** @name HSCOM Power state mask + * + * This register contains HSCOM_PWR to check powerup_state. + * @{ + */ + +#define XRFDC_HSCOM_PWR_STATE_MASK 0x0000FFFFU /**< powerup state mask */ + +/* @} */ + +/** @name Interpolation Control + * + * This register contains Interpolation filter modes. + * @{ + */ + +#define XRFDC_INTERP_MODE_MASK 0x00000077U /**< Interp filter mask */ +#define XRFDC_INTERP_MODE_I_MASK 0x00000007U /**< Interp filter I */ +#define XRFDC_INTERP_MODE_Q_SHIFT 4U /**< Interp mode Q shift */ +#define XRFDC_INTERP_MODE_MASK_EXT 0x00003F3FU /**< Interp filter mask */ +#define XRFDC_INTERP_MODE_I_MASK_EXT 0x0000003FU /**< Interp filter I */ +#define XRFDC_INTERP_MODE_Q_SHIFT_EXT 8U /**< Interp mode Q shift */ + + +/* @} */ + +/** @name Tile Reset + * + * This register contains Tile reset bit. + * @{ + */ + +#define XRFDC_TILE_RESET_MASK 0x00000001U /**< Tile reset mask */ + +/* @} */ + +/** @name Status register + * + * This register contains common status bits. + * @{ + */ + +#define XRFDC_PWR_UP_STAT_MASK 0x00000004U /**< Power Up state mask */ +#define XRFDC_PWR_UP_STAT_SHIFT 2U /**< PowerUp status shift */ +#define XRFDC_PLL_LOCKED_MASK 0x00000008U /**< PLL Locked mask */ +#define XRFDC_PLL_LOCKED_SHIFT 3U /**< PLL locked shift */ + +/* @} */ + +/** @name Restart State register + * + * This register contains Start and End state bits. + * @{ + */ + +#define XRFDC_PWR_STATE_MASK 0x0000FFFFU /**< State mask */ +#define XRFDC_RSR_START_SHIFT 8U /**< Start state shift */ + + +/* @} */ + +/** @name Clock Detect register + * + * This register contains Start and End state bits. + * @{ + */ + +#define XRFDC_CLOCK_DETECT_MASK 0x0000FFFFU /**< Clock detect mask */ + + +/* @} */ + +/** @name Common interrupt enable register + * + * This register contains bits to enable interrupt for + * ADC and DAC tiles. + * @{ + */ + +#define XRFDC_EN_INTR_DAC_TILE0_MASK 0x00000001U /**< DAC Tile0 + interrupt enable mask */ +#define XRFDC_EN_INTR_DAC_TILE1_MASK 0x00000002U /**< DAC Tile1 + interrupt enable mask */ +#define XRFDC_EN_INTR_DAC_TILE2_MASK 0x00000004U /**< DAC Tile2 + interrupt enable mask */ +#define XRFDC_EN_INTR_DAC_TILE3_MASK 0x00000008U /**< DAC Tile3 + interrupt enable mask */ +#define XRFDC_EN_INTR_ADC_TILE0_MASK 0x00000010U /**< ADC Tile0 + interrupt enable mask */ +#define XRFDC_EN_INTR_ADC_TILE1_MASK 0x00000020U /**< ADC Tile1 + interrupt enable mask */ +#define XRFDC_EN_INTR_ADC_TILE2_MASK 0x00000040U /**< ADC Tile2 + interrupt enable mask */ +#define XRFDC_EN_INTR_ADC_TILE3_MASK 0x00000080U /**< ADC Tile3 + interrupt enable mask */ +/* @} */ + + +/** @name interrupt enable register + * + * This register contains bits to enable interrupt for blocks. + * @{ + */ + +#define XRFDC_EN_INTR_SLICE_MASK 0x0000000FU /**< Slice intr mask */ +#define XRFDC_EN_INTR_SLICE0_MASK 0x00000001U /**< slice0 + interrupt enable mask */ +#define XRFDC_EN_INTR_SLICE1_MASK 0x00000002U /**< slice1 + interrupt enable mask */ +#define XRFDC_EN_INTR_SLICE2_MASK 0x00000004U /**< slice2 + interrupt enable mask */ +#define XRFDC_EN_INTR_SLICE3_MASK 0x00000008U /**< slice3 + interrupt enable mask */ +/* @} */ + +/** @name Converter(X) interrupt register + * + * This register contains bits to enable different interrupts for block X. + * @{ + */ + +#define XRFDC_INTR_OVR_RANGE_MASK 0x00000008U /**< Over Range + interrupt mask */ +#define XRFDC_INTR_OVR_VOLTAGE_MASK 0x00000004U /**< Over Voltage + interrupt mask */ +#define XRFDC_INTR_FIFO_OVR_MASK 0x00008000U /**< FIFO OF mask */ +#define XRFDC_INTR_DAT_OVR_MASK 0x00004000U /**< Data OF mask */ +#define XRFDC_INTR_CMODE_OVR_MASK 0x00040000U /**< Common mode OV mask */ +#define XRFDC_INTR_CMODE_UNDR_MASK 0x00080000U /**< Common mode UV mask */ +/* @} */ + +/** @name Multiband config register + * + * This register contains bits to configure multiband. + * @{ + */ + +#define XRFDC_EN_MB_MASK 0x00000008U /**< multi-band adder mask */ +#define XRFDC_EN_MB_SHIFT 3U /** <Enable Multiband shift */ +#define XRFDC_ALT_BOND_MASK 0x0100 /** <Alt bondout mask */ +#define XRFDC_ALT_BOND_SHIFT 8U /** <Alt bondout shift */ + +/* @} */ + +/** @name Invsinc control register + * + * This register contains bits to configure Invsinc. + * @{ + */ +#define XRFDC_EN_INVSINC_MASK 0x00000001U /**< invsinc enable mask */ +#define XRFDC_MODE_INVSINC_MASK 0x00000003U /**< invsinc mode mask */ +/* @} */ + +/* @} */ + +/** @name Signal Detector control register + * + * This register contains bits to configure Signal Detector. + * @{ + */ +#define XRFDC_ADC_SIG_DETECT_MASK 0xFF /**< signal detector mask */ +#define XRFDC_ADC_SIG_DETECT_THRESH_MASK 0xFF /**< signal detector thresholds mask */ +#define XRFDC_ADC_SIG_DETECT_INTG_MASK 0x01 /**< leaky integrator enable mask */ +#define XRFDC_ADC_SIG_DETECT_FLUSH_MASK 0x02 /**< leaky integrator flush mask */ +#define XRFDC_ADC_SIG_DETECT_TCONST_MASK 0x1C /**< time constant mask */ +#define XRFDC_ADC_SIG_DETECT_MODE_MASK 0x60 /**< mode mask */ +#define XRFDC_ADC_SIG_DETECT_HYST_MASK 0x80 /**< hysteresis enable mask */ +#define XRFDC_ADC_SIG_DETECT_INTG_SHIFT 0 /**< leaky integrator enable shift */ +#define XRFDC_ADC_SIG_DETECT_FLUSH_SHIFT 1 /**< leaky integrator flush shift */ +#define XRFDC_ADC_SIG_DETECT_TCONST_SHIFT 2 /**< time constant shift */ +#define XRFDC_ADC_SIG_DETECT_MODE_WRITE_SHIFT 5 /**< mode shift fror writing */ +#define XRFDC_ADC_SIG_DETECT_MODE_READ_SHIFT 6 /**< mode shift fror reading */ +#define XRFDC_ADC_SIG_DETECT_HYST_SHIFT 7 /**< hysteresis enable shift */ +/* @} */ + +/** @name CLK_DIV register + * + * This register contains the bits to control the clock + * divider providing the clock fabric out. + * @{ + */ + +#define XRFDC_FAB_CLK_DIV_MASK 0x0000000FU /**< clk div mask */ + +/* @} */ + +/** @name Multiband Config + * + * This register contains bits to configure multiband for DAC. + * @{ + */ + +#define XRFDC_MB_CFG_MASK 0x000001FFU /**< MB config mask */ +#define XRFDC_MB_EN_4X_MASK 0x00000100U /**< Enable 4X MB mask */ + +/* @} */ + +/** @name Multi Tile Sync + * + * Multi-Tile Sync bit masks. + * @{ + */ + +#define XRFDC_MTS_SRCAP_PLL_M 0x0100U +#define XRFDC_MTS_SRCAP_DIG_M 0x0100U +#define XRFDC_MTS_SRCAP_EN_TRX_M 0x0400U +#define XRFDC_MTS_SRCAP_INIT_M 0x8200U +#define XRFDC_MTS_SRCLR_T1_M 0x2000U +#define XRFDC_MTS_SRCLR_PLL_M 0x0200U +#define XRFDC_MTS_PLLEN_M 0x0001U +#define XRFDC_MTS_SRCOUNT_M 0x00FFU +#define XRFDC_MTS_DELAY_VAL_M 0x041FU +#define XRFDC_MTS_AMARK_CNT_M 0x00FFU +#define XRFDC_MTS_AMARK_LOC_M 0x0F0000U +#define XRFDC_MTS_AMARK_DONE_M 0x100000U + +/* @} */ + +/** @name Output divider LSB register + * + * This register contains bits to configure output divisor + * @{ + */ + +#define XRFDC_PLL_DIVIDER0_MASK 0x00FFU +#define XRFDC_PLL_DIVIDER0_MODE_MASK 0x00C0U +#define XRFDC_PLL_DIVIDER0_VALUE_MASK 0x003FU +#define XRFDC_PLL_DIVIDER0_SHIFT 6U + +/* @} */ + +/** @name Multi-tile sync and clock source control register + * + * This register contains bits to Multi-tile sync and clock source control + * @{ + */ +#define XRFDC_CLK_NETWORK_CTRL1_USE_PLL_MASK 0x1U /**< PLL clock mask */ + +/* @} */ + +/** @name PLL_CRS1 - PLL CRS1 register + * + * This register contains bits for VCO sel_auto, VCO band selection etc., + * @{ + */ + +#define XRFDC_PLL_CRS1_VCO_SEL_MASK 0x00008001U /**< VCO SEL Mask */ +#define XRFDC_PLL_VCO_SEL_AUTO_MASK 0x00008000U /**< VCO Auto SEL Mask */ + +/* @} */ + +/** Register bits Shift, Width Masks + * + * @{ + */ +#define XRFDC_DIGI_ANALOG_SHIFT4 4U +#define XRFDC_DIGI_ANALOG_SHIFT8 8U +#define XRFDC_DIGI_ANALOG_SHIFT12 12U + +/* @} */ + +#define XRFDC_IXR_FIFOUSRDAT_MASK 0x0000000FU +#define XRFDC_IXR_FIFOUSRDAT_OF_MASK 0x00000001U +#define XRFDC_IXR_FIFOUSRDAT_UF_MASK 0x00000002U +#define XRFDC_IXR_FIFOMRGNIND_OF_MASK 0x00000004U +#define XRFDC_IXR_FIFOMRGNIND_UF_MASK 0x00000008U +#define XRFDC_ADC_IXR_DATAPATH_MASK 0x00000FF0U +#define XRFDC_ADC_IXR_DMON_STG_MASK 0x000003F0U +#define XRFDC_DAC_IXR_DATAPATH_MASK 0x00001FF0U +#define XRFDC_DAC_IXR_INTP_STG_MASK 0x000003F0U +#define XRFDC_DAC_IXR_INTP_I_STG0_MASK 0x00000010U +#define XRFDC_DAC_IXR_INTP_I_STG1_MASK 0x00000020U +#define XRFDC_DAC_IXR_INTP_I_STG2_MASK 0x00000040U +#define XRFDC_DAC_IXR_INTP_Q_STG0_MASK 0x00000080U +#define XRFDC_DAC_IXR_INTP_Q_STG1_MASK 0x00000100U +#define XRFDC_DAC_IXR_INTP_Q_STG2_MASK 0x00000200U +#define XRFDC_ADC_IXR_DMON_I_STG0_MASK 0x00000010U +#define XRFDC_ADC_IXR_DMON_I_STG1_MASK 0x00000020U +#define XRFDC_ADC_IXR_DMON_I_STG2_MASK 0x00000040U +#define XRFDC_ADC_IXR_DMON_Q_STG0_MASK 0x00000080U +#define XRFDC_ADC_IXR_DMON_Q_STG1_MASK 0x00000100U +#define XRFDC_ADC_IXR_DMON_Q_STG2_MASK 0x00000200U +#define XRFDC_IXR_QMC_GAIN_PHASE_MASK 0x00000400U +#define XRFDC_IXR_QMC_OFFST_MASK 0x00000800U +#define XRFDC_DAC_IXR_INVSNC_OF_MASK 0x00001000U +#define XRFDC_SUBADC_IXR_DCDR_MASK 0x00FF0000U +#define XRFDC_SUBADC0_IXR_DCDR_OF_MASK 0x00010000U +#define XRFDC_SUBADC0_IXR_DCDR_UF_MASK 0x00020000U +#define XRFDC_SUBADC1_IXR_DCDR_OF_MASK 0x00040000U +#define XRFDC_SUBADC1_IXR_DCDR_UF_MASK 0x00080000U +#define XRFDC_SUBADC2_IXR_DCDR_OF_MASK 0x00100000U +#define XRFDC_SUBADC2_IXR_DCDR_UF_MASK 0x00200000U +#define XRFDC_SUBADC3_IXR_DCDR_OF_MASK 0x00400000U +#define XRFDC_SUBADC3_IXR_DCDR_UF_MASK 0x00800000U +#define XRFDC_ADC_OVR_VOLTAGE_MASK 0x04000000U +#define XRFDC_ADC_OVR_RANGE_MASK 0x08000000U +#define XRFDC_ADC_CMODE_OVR_MASK 0x10000000U +#define XRFDC_ADC_CMODE_UNDR_MASK 0x20000000U +#define XRFDC_ADC_DAT_OVR_MASK 0x40000000U +#define XRFDC_ADC_FIFO_OVR_MASK 0x80000000U +#define XRFDC_DAC_MC_CFG2_OPCSCAS_MASK 0x0000F8F8U +#define XRFDC_DAC_MC_CFG3_CSGAIN_MASK 0x0000FFC0U +#define XRFDC_DAC_MC_CFG2_OPCSCAS_20MA 0x00004858U +#define XRFDC_DAC_MC_CFG3_CSGAIN_20MA 0x000087C0U +#define XRFDC_DAC_MC_CFG2_OPCSCAS_32MA 0x0000A0D8U +#define XRFDC_DAC_MC_CFG3_CSGAIN_32MA 0x0000FFC0U + +#define XRFDC_ADC_OVR_VOL_RANGE_SHIFT 24U +#define XRFDC_ADC_DAT_FIFO_OVR_SHIFT 16U +#define XRFDC_ADC_SUBADC_DCDR_SHIFT 16U +#define XRFDC_DATA_PATH_SHIFT 4U +#define XRFDC_ADC_CMODE_SHIFT 10U + +#define XRFDC_DAC_TILE_DRP_ADDR(X) (0x6000U + (X * 0x4000U)) +#define XRFDC_DAC_TILE_CTRL_STATS_ADDR(X) (0x4000U + (X * 0x4000U)) +#define XRFDC_ADC_TILE_DRP_ADDR(X) (0x16000U + (X * 0x4000U)) +#define XRFDC_ADC_TILE_CTRL_STATS_ADDR(X) (0x14000U + (X * 0x4000U)) +#define XRFDC_CTRL_STATS_OFFSET 0x0U +#define XRFDC_HSCOM_ADDR 0x1C00U +#define XRFDC_BLOCK_ADDR_OFFSET(X) (X * 0x400U) +#define XRFDC_TILE_DRP_OFFSET 0x2000U + + +/***************** Macros (Inline Functions) Definitions *********************/ +#define XRFdc_In64 metal_io_read64 +#define XRFdc_Out64 metal_io_write64 + +#define XRFdc_In32 metal_io_read32 +#define XRFdc_Out32 metal_io_write32 + +#define XRFdc_In16 metal_io_read16 +#define XRFdc_Out16 metal_io_write16 + +#define XRFdc_In8 metal_io_read8 +#define XRFdc_Out8 metal_io_write8 + +/****************************************************************************/ +/** +* Read a register. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to the target register. +* +* @return The value read from the register. +* +* @note C-Style signature: +* u32 XRFdc_ReadReg64(XRFdc *InstancePtr. u32 BaseAddress, s32 RegOffset) +* +******************************************************************************/ +#define XRFdc_ReadReg64(InstancePtr, BaseAddress, RegOffset) \ + XRFdc_In64(InstancePtr->io, ((u32)RegOffset + (u32)BaseAddress)) + +/***************************************************************************/ +/** +* Write to a register. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to target register. +* @param RegisterValue is the value to be written to the register. +* +* @return None. +* +* @note C-Style signature: +* void XRFdc_WriteReg64(XRFdc *InstancePtr, u32 BaseAddress, s32 RegOffset, +* u64 RegisterValue) +* +******************************************************************************/ +#define XRFdc_WriteReg64(InstancePtr, BaseAddress, RegOffset, RegisterValue) \ + XRFdc_Out64((InstancePtr->io), ((u32)RegOffset + (u32)BaseAddress), \ + (u32)(RegisterValue)) + +/****************************************************************************/ +/** +* Read a register. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to the target register. +* +* @return The value read from the register. +* +* @note C-Style signature: +* u32 XRFdc_ReadReg(XRFdc *InstancePtr, u32 BaseAddress. int RegOffset) +* +******************************************************************************/ +#define XRFdc_ReadReg(InstancePtr, BaseAddress, RegOffset) \ + XRFdc_In32((InstancePtr->io), ((u32)BaseAddress + (u32)RegOffset)) + +/***************************************************************************/ +/** +* Write to a register. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to target register. +* @param RegisterValue is the value to be written to the register. +* +* @return None. +* +* @note C-Style signature: +* void XRFdc_WriteReg(XRFdc *InstancePtr, u32 BaseAddress, int RegOffset, +* u32 RegisterValue) +* +******************************************************************************/ +#define XRFdc_WriteReg(InstancePtr, BaseAddress, RegOffset, RegisterValue) \ + XRFdc_Out32((InstancePtr->io), ((u32)RegOffset + (u32)BaseAddress), \ + (u32)(RegisterValue)) + +/****************************************************************************/ +/** +* Read a register. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to the target register. +* +* @return The value read from the register. +* +* @note C-Style signature: +* u16 XRFdc_ReadReg16(XRFdc *InstancePtr, u32 BaseAddress. int RegOffset) +* +******************************************************************************/ +#define XRFdc_ReadReg16(InstancePtr, BaseAddress, RegOffset) \ + XRFdc_In16((InstancePtr->io), ((u32)RegOffset + (u32)BaseAddress)) + +/***************************************************************************/ +/** +* Write to a register. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to target register. +* @param RegisterValue is the value to be written to the register. +* +* @return None. +* +* @note C-Style signature: +* void XRFdc_WriteReg16(XRFdc *InstancePtr, u32 BaseAddress, int RegOffset, +* u16 RegisterValue) +* +******************************************************************************/ +#define XRFdc_WriteReg16(InstancePtr, BaseAddress, RegOffset, RegisterValue) \ + XRFdc_Out16((InstancePtr->io), ((u32)RegOffset + (u32)BaseAddress), \ + (u32)(RegisterValue)) + +/****************************************************************************/ +/** +* Read a register. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to the target register. +* +* @return The value read from the register. +* +* @note C-Style signature: +* u8 XRFdc_ReadReg8(XRFdc *InstancePtr, u32 BaseAddress. int RegOffset) +* +******************************************************************************/ +#define XRFdc_ReadReg8(InstancePtr, BaseAddress, RegOffset) \ + XRFdc_In8((InstancePtr->io), ((u32)RegOffset + (u32)BaseAddress)) + +/***************************************************************************/ +/** +* Write to a register. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to target register. +* @param RegisterValue is the value to be written to the register. +* +* @return None. +* +* @note C-Style signature: +* void XRFdc_WriteReg8(XRFdc *InstancePtr, u32 BaseAddress, int RegOffset, +* u8 RegisterValue) +* +******************************************************************************/ +#define XRFdc_WriteReg8(InstancePtr, BaseAddress, RegOffset, RegisterValue) \ + XRFdc_Out8((InstancePtr->io), ((u32)RegOffset + (u32)BaseAddress), \ + (u32)(RegisterValue)) + +#ifdef __cplusplus +} +#endif + +#endif /* RFDC_HW_H_ */ +/** @} */ diff --git a/mpm/include/mpm/rfdc/xrfdc_mts.h b/mpm/include/mpm/rfdc/xrfdc_mts.h new file mode 100644 index 000000000..ef56a6193 --- /dev/null +++ b/mpm/include/mpm/rfdc/xrfdc_mts.h @@ -0,0 +1,166 @@ +/****************************************************************************** +* +* Copyright (C) 2018-2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc_mts.c +* @addtogroup xrfdc_v6_0 +* @{ +* +* Contains the multi tile sync related structures, Macros of the XRFdc driver. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 3.1 jm 01/24/18 Initial release +* 3.2 jm 03/12/18 Fixed DAC latency calculation. +* jm 03/12/18 Added support for reloading DTC scans. +* jm 03/12/18 Add option to configure sysref capture after MTS. +* 4.0 sk 04/09/18 Added API to enable/disable the sysref. +* rk 04/17/18 Adjust calculated latency by sysref period, where doing +* so results in closer alignment to the target latency. +* 5.0 sk 08/03/18 Fixed MISRAC warnings. +* sk 08/03/18 Check for Block0 enable for tiles participating in MTS. +* sk 08/24/18 Reorganize the code to improve readability and +* optimization. +* 6.0 cog 02/17/19 Added XRFdc_GetMTSEnable API. +* +* </pre> +* +******************************************************************************/ +#ifndef RFDC_MTS_H_ +#define RFDC_MTS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************** Include Files *********************************/ + +#include "xrfdc.h" + +/************************** Constant Definitions *****************************/ + +#define XRFDC_MTS_RMW(read, mask, data) (((read) & ~(mask)) | ((data) & (mask))) +#define XRFDC_MTS_FIELD(data, mask, shift) (((data) & (mask)) >> (shift)) + +/**************************** Type Definitions *******************************/ + +typedef struct { + u32 RefTile; + u32 IsPLL; + int Target[4]; + int Scan_Mode; + int DTC_Code[4]; + int Num_Windows[4]; + int Max_Gap[4]; + int Min_Gap[4]; + int Max_Overlap[4]; +} XRFdc_MTS_DTC_Settings; + +typedef struct { + u32 RefTile; + u32 Tiles; + int Target_Latency; + int Offset[4]; + int Latency[4]; + int Marker_Delay; + int SysRef_Enable; + XRFdc_MTS_DTC_Settings DTC_Set_PLL; + XRFdc_MTS_DTC_Settings DTC_Set_T1; +} XRFdc_MultiConverter_Sync_Config; + +typedef struct { + u32 Count[4]; + u32 Loc[4]; +} XRFdc_MTS_Marker; + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define XRFDC_MTS_SYSREF_DISABLE 0U +#define XRFDC_MTS_SYSREF_ENABLE 1U + +#define XRFDC_MTS_NUM_DTC 128U +#define XRFDC_MTS_REF_TARGET 64U +#define XRFDC_MTS_MAX_CODE 16U +#define XRFDC_MTS_MIN_GAP_T1 10U +#define XRFDC_MTS_MIN_GAP_PLL 5U +#define XRFDC_MTS_SR_TIMEOUT 4096U +#define XRFDC_MTS_DTC_COUNT 10U +#define XRFDC_MTS_MARKER_COUNT 4U +#define XRFDC_MTS_SCAN_INIT 0U +#define XRFDC_MTS_SCAN_RELOAD 1U +#define XRFDC_MTS_SRCOUNT_TIMEOUT 1000U +#define XRFDC_MTS_DELAY_MAX 31U +#define XRFDC_MTS_CHECK_ALL_FIFOS 0U + +#define XRFDC_MTS_SRCAP_T1_EN 0x4000U +#define XRFDC_MTS_SRCAP_T1_RST 0x0800U +#define XRFDC_MTS_SRFLAG_T1 0x4U +#define XRFDC_MTS_SRFLAG_PLL 0x2U +#define XRFDC_MTS_FIFO_DEFAULT 0x0000U +#define XRFDC_MTS_FIFO_ENABLE 0x0003U +#define XRFDC_MTS_FIFO_DISABLE 0x0002U +#define XRFDC_MTS_AMARK_LOC_S 0x10U +#define XRFDC_MTS_AMARK_DONE_S 0x14U +#define XRFDC_MTS_DLY_ALIGNER 0x28U + +/* Error Codes */ +#define XRFDC_MTS_OK 0U +#define XRFDC_MTS_NOT_SUPPORTED 1U +#define XRFDC_MTS_TIMEOUT 2U +#define XRFDC_MTS_MARKER_RUN 4U +#define XRFDC_MTS_MARKER_MISM 8U +#define XRFDC_MTS_DELAY_OVER 16U +#define XRFDC_MTS_TARGET_LOW 32U +#define XRFDC_MTS_IP_NOT_READY 64U +#define XRFDC_MTS_DTC_INVALID 128U +#define XRFDC_MTS_NOT_ENABLED 512U +#define XRFDC_MTS_SYSREF_GATE_ERROR 2048U +#define XRFDC_MTS_SYSREF_FREQ_NDONE 4096U + +/************************** Function Prototypes ******************************/ + +u32 XRFdc_MultiConverter_Sync(XRFdc *InstancePtr, u32 Type, + XRFdc_MultiConverter_Sync_Config *ConfigPtr); +void XRFdc_MultiConverter_Init(XRFdc_MultiConverter_Sync_Config *ConfigPtr, + int *PLL_CodesPtr, int *T1_CodesPtr); +u32 XRFdc_MTS_Sysref_Config(XRFdc *InstancePtr, + XRFdc_MultiConverter_Sync_Config *DACSyncConfigPtr, + XRFdc_MultiConverter_Sync_Config *ADCSyncConfigPtr, u32 SysRefEnable); +u32 XRFdc_GetMTSEnable(XRFdc *InstancePtr, u32 Type,u32 Tile, u32 *EnablePtr); + + +#ifdef __cplusplus +} +#endif + +#endif /* RFDC_MTS_H_ */ +/** @} */ diff --git a/mpm/lib/CMakeLists.txt b/mpm/lib/CMakeLists.txt index 940a23138..f127af0a6 100644 --- a/mpm/lib/CMakeLists.txt +++ b/mpm/lib/CMakeLists.txt @@ -18,6 +18,10 @@ elseif(ENABLE_E320 OR ENABLE_E300) add_subdirectory(catalina) endif(ENABLE_MYKONOS) +if(ENABLE_X400) + add_subdirectory(rfdc) +endif(ENABLE_X400) + USRP_PERIPHS_ADD_OBJECT(periphs exception.cpp ${UHD_HOST_ROOT}/lib/exception.cpp diff --git a/mpm/lib/i2c/i2cdev_iface.cpp b/mpm/lib/i2c/i2cdev_iface.cpp index 9d2c6b4b2..b723dea06 100644 --- a/mpm/lib/i2c/i2cdev_iface.cpp +++ b/mpm/lib/i2c/i2cdev_iface.cpp @@ -64,23 +64,29 @@ public: return ret; } - int transfer(std::vector<uint8_t>* tx, std::vector<uint8_t>* rx, bool do_close) + std::vector<uint8_t> transfer( + std::vector<uint8_t>& tx, size_t num_rx_bytes, bool do_close) { uint8_t *tx_data = NULL, *rx_data = NULL; size_t tx_len = 0, rx_len = 0; + std::vector<uint8_t> rx(num_rx_bytes); - if (tx) { - tx_data = tx->data(); - tx_len = tx->size(); + if (!tx.empty()) { + tx_data = tx.data(); + tx_len = tx.size(); } - if (rx) { - rx_data = rx->data(); - rx_len = rx->size(); + if (num_rx_bytes) { + rx_data = rx.data(); + rx_len = rx.size(); } - int ret = transfer(tx_data, tx_len, rx_data, rx_len, do_close); - return ret; + const int err = transfer(tx_data, tx_len, rx_data, num_rx_bytes, do_close); + if (err) { + throw mpm::runtime_error("I2C Transaction failed!"); + } + + return rx; } private: diff --git a/mpm/lib/rfdc/CMakeLists.txt b/mpm/lib/rfdc/CMakeLists.txt new file mode 100644 index 000000000..a7401fd7b --- /dev/null +++ b/mpm/lib/rfdc/CMakeLists.txt @@ -0,0 +1,19 @@ +# +# Copyright 2019 Ettus Research, National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0 +# +set(RFDC_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/rfdc_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rfdc_throw.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/xrfdc_clock.c + ${CMAKE_CURRENT_SOURCE_DIR}/xrfdc_g.c + ${CMAKE_CURRENT_SOURCE_DIR}/xrfdc_intr.c + ${CMAKE_CURRENT_SOURCE_DIR}/xrfdc_mb.c + ${CMAKE_CURRENT_SOURCE_DIR}/xrfdc_mixer.c + ${CMAKE_CURRENT_SOURCE_DIR}/xrfdc_mts.c + ${CMAKE_CURRENT_SOURCE_DIR}/xrfdc_sinit.c + ${CMAKE_CURRENT_SOURCE_DIR}/xrfdc.c +) + +USRP_PERIPHS_ADD_OBJECT(rfdc ${RFDC_SOURCES}) diff --git a/mpm/lib/rfdc/README.md b/mpm/lib/rfdc/README.md new file mode 100644 index 000000000..ecf4fc054 --- /dev/null +++ b/mpm/lib/rfdc/README.md @@ -0,0 +1,6 @@ +The `xrfdc*.c` files here (and the files `mpm/include/mpm/rfdc/xrfdc*.h`) are derived from files in the `xilinx-v2019.1` tag of [embeddedsw](https://github.com/Xilinx/embeddedsw/), with the following changes: +* Include paths +* `Xil_Assert` macros in `xrfdc.h` were replaced with custom functionality. + * See `patches/xrfdc.h.patch` for the patch applied to that file. +* Call `closedir` in `xrfdc_sinit.c` + * See `patches/xrfdc_sinic.c.patch` for the patch diff --git a/mpm/lib/rfdc/patches/xrfdc.h.patch b/mpm/lib/rfdc/patches/xrfdc.h.patch new file mode 100644 index 000000000..d2fceb13a --- /dev/null +++ b/mpm/lib/rfdc/patches/xrfdc.h.patch @@ -0,0 +1,53 @@ +--- embeddedsw/XilinxProcessorIPLib/drivers/rfdc/src/xrfdc.h 2020-07-16 16:23:14.839402600 -0500 ++++ uhddev/mpm/include/mpm/rfdc/xrfdc.h 2020-08-17 12:31:24.477432400 -0500 +@@ -235,6 +235,7 @@ + + /***************************** Include Files *********************************/ + ++#include "rfdc_throw.h" + #include <stdlib.h> + #include <stdint.h> + +@@ -650,24 +651,24 @@ + + /***************** Macros (Inline Functions) Definitions *********************/ + +-#ifndef __BAREMETAL__ +-#define Xil_AssertNonvoid(Expression) \ +-{ \ +- if (!(Expression)) { \ +- while (1); \ +- } \ +-} +-#define Xil_AssertVoid(Expression) \ +-{ \ +- if (!(Expression)) { \ +- while (1); \ +- } \ +-} +-#define Xil_AssertVoidAlways() \ +-{ \ +- while (1); \ +-} +-#endif ++# ifndef __BAREMETAL__ ++# define Xil_AssertNonvoid(Expression) \ ++ { \ ++ if (!(Expression)) { \ ++ rfdc_throw(#Expression); \ ++ } \ ++ } ++# define Xil_AssertVoid(Expression) \ ++ { \ ++ if (!(Expression)) { \ ++ rfdc_throw(#Expression); \ ++ } \ ++ } ++# define Xil_AssertVoidAlways() \ ++ { \ ++ rfdc_throw("Assert false"); \ ++ } ++# endif + + #define MAX(x,y) (x>y)?x:y + #define MIN(x,y) (x<y)?x:y diff --git a/mpm/lib/rfdc/patches/xrfdc_sinit.c.patch b/mpm/lib/rfdc/patches/xrfdc_sinit.c.patch new file mode 100644 index 000000000..69a97e5df --- /dev/null +++ b/mpm/lib/rfdc/patches/xrfdc_sinit.c.patch @@ -0,0 +1,10 @@ +--- ../embeddedsw/XilinxProcessorIPLib/drivers/rfdc/src/xrfdc_sinit.c 2021-01-14 10:22:52.195957400 -0600 ++++ mpm/lib/rfdc/xrfdc_sinit.c 2021-01-14 10:24:59.611972600 -0600 +@@ -180,6 +180,7 @@ + metal_device_close(DevicePtr); + } + } ++ closedir(DirPtr); + } + return Status; + } diff --git a/mpm/lib/rfdc/rfdc_ctrl.cpp b/mpm/lib/rfdc/rfdc_ctrl.cpp new file mode 100644 index 000000000..94cd85c73 --- /dev/null +++ b/mpm/lib/rfdc/rfdc_ctrl.cpp @@ -0,0 +1,745 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "mpm/rfdc/rfdc_ctrl.hpp" +#include <mpm/exception.hpp> +#include <set> + +#define BUS_NAME "platform" + +namespace mpm { namespace rfdc { + +rfdc_ctrl::rfdc_ctrl() +{ + rfdc_inst_ptr = &rfdc_inst; + rfdc_inst_ptr->device = nullptr; + rfdc_inst_ptr->io = nullptr; + + // Populates default values to the struct + XRFdc_MultiConverter_Init(&rfdc_dac_sync_config, nullptr, nullptr); + XRFdc_MultiConverter_Init(&rfdc_adc_sync_config, nullptr, nullptr); +} + +rfdc_ctrl::~rfdc_ctrl() +{ + if (rfdc_inst_ptr && rfdc_inst_ptr->device) { + metal_device_close(rfdc_inst_ptr->device); + } + if (metal_init_complete) { + metal_finish(); + } +} + +void rfdc_ctrl::init(uint16_t rfdc_device_id) +{ + XRFdc_Config* config_ptr; + char device_name[NAME_MAX]; + + this->rfdc_device_id = rfdc_device_id; + + struct metal_init_params init_param = METAL_INIT_DEFAULTS; + + if (metal_init(&init_param)) { + throw mpm::runtime_error("Failed to run metal initialization for rfdc.\n"); + } + metal_init_complete = true; + + /* Get configuration data for te RFdc device */ + /* config_ptr is an entry of the XRFdc_ConfigTablePtr array managed by xrfdc_sinit.c + * This memory is not explicitly freed because we do not have access to + * XRFdc_ConfigTablePtr in this scope. */ + config_ptr = XRFdc_LookupConfig(rfdc_device_id); + if (config_ptr == NULL) { + throw mpm::runtime_error("Rfdc config lookup failed.\n"); + } + + /* Initializes the controller with loaded config information */ + if (XRFdc_CfgInitialize(rfdc_inst_ptr, config_ptr) != XRFDC_SUCCESS) { + throw mpm::runtime_error("Rfdc controller init failed.\n"); + } + + /* Set UpdateMixerScale into valid state. For some reason the + XRFdc config functions do not set this value. It will be + overwritten when XRFdc_SetMixerSettings is called next. */ + rfdc_inst_ptr->UpdateMixerScale = 0; + + if (XRFdc_GetDeviceNameByDeviceId(device_name, rfdc_device_id) < 0) { + throw mpm::runtime_error("Failed to find rfdc device with device id \n"); + } + + if (metal_device_open(BUS_NAME, device_name, &rfdc_inst_ptr->device)) { + throw mpm::runtime_error("Failed to open device.\n"); + } + + /* Map RFDC device IO region. 0 is the IO region index on the device. */ + rfdc_inst_ptr->io = metal_device_io_region(rfdc_inst_ptr->device, 0); + if (!rfdc_inst_ptr->io) { + throw mpm::runtime_error("Failed to map RFDC regio\n"); + } + + /* Set all gain threshold stickies to manual clear mode */ + for (int tile_id = 0; tile_id <= XRFDC_TILE_ID_MAX; tile_id++) { + for (int block_id = 0; block_id <= XRFDC_BLOCK_ID_MAX; block_id++) { + for (int threshold_id = 0; threshold_id < THRESHOLDS_PER_BLOCK; + threshold_id++) { + threshold_clr_modes[tile_id][block_id][threshold_id] = + THRESHOLD_CLRMD_UNKNOWN; + } + set_threshold_clr_mode( + tile_id, block_id, THRESHOLD_BOTH, THRESHOLD_CLRMD_MANUAL); + } + } +} + +bool rfdc_ctrl::startup_tile(int tile_id, bool is_dac) +{ + return XRFdc_StartUp(rfdc_inst_ptr, is_dac, tile_id) == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::shutdown_tile(int tile_id, bool is_dac) +{ + return XRFdc_Shutdown(rfdc_inst_ptr, is_dac, tile_id) == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::reset_tile(int tile_id, bool is_dac) +{ + return XRFdc_Reset(rfdc_inst_ptr, is_dac, tile_id) == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::trigger_update_event( + uint32_t tile_id, uint32_t block_id, bool is_dac, event_type_options event_type) +{ + return XRFdc_UpdateEvent(rfdc_inst_ptr, is_dac, tile_id, block_id, event_type) + == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::reset_mixer_settings(uint32_t tile_id, uint32_t block_id, bool is_dac) +{ + XRFdc_Mixer_Settings mixer_settings; + + mixer_settings.Freq = 200; + mixer_settings.PhaseOffset = 0; + mixer_settings.EventSource = XRFDC_EVNT_SRC_SYSREF; + mixer_settings.CoarseMixFreq = 16; + mixer_settings.MixerMode = is_dac ? MIXER_MODE_C2R : MIXER_MODE_R2C; + mixer_settings.FineMixerScale = 0; + mixer_settings.MixerType = XRFDC_MIXER_TYPE_FINE; + + return (XRFdc_SetMixerSettings( + rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + == XRFDC_SUCCESS); +} + +bool rfdc_ctrl::set_gain_enable( + uint32_t tile_id, uint32_t block_id, bool is_dac, bool enable) +{ + XRFdc_QMC_Settings qmc_settings; + + // Get current QMC settings for the values that will not be changed + if (XRFdc_GetQMCSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &qmc_settings) + != XRFDC_SUCCESS) { + return false; + } + + qmc_settings.EnableGain = enable; + // Update the setting on a SYSREF trigger + qmc_settings.EventSource = XRFDC_EVNT_SRC_SYSREF; + + return (XRFdc_SetQMCSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &qmc_settings) + == XRFDC_SUCCESS); +} + +bool rfdc_ctrl::set_gain(uint32_t tile_id, uint32_t block_id, bool is_dac, double gain) +{ + XRFdc_QMC_Settings qmc_settings; + + // Get current QMC settings for the values that will not be changed + if (XRFdc_GetQMCSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &qmc_settings) + != XRFDC_SUCCESS) { + return false; + } + + qmc_settings.EnableGain = 1; + qmc_settings.GainCorrectionFactor = gain; + // Update the setting on a SYSREF trigger + qmc_settings.EventSource = XRFDC_EVNT_SRC_SYSREF; + + return (XRFdc_SetQMCSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &qmc_settings) + == XRFDC_SUCCESS); +} + +bool rfdc_ctrl::set_threshold_settings(uint32_t tile_id, + uint32_t block_id, + threshold_id_options threshold_id, + threshold_mode_options mode, + uint32_t average_val, + uint32_t under_val, + uint32_t over_val) +{ + XRFdc_Threshold_Settings threshold_settings; + + // Get current threshold settings for the values that will not be changed + if (XRFdc_GetThresholdSettings(rfdc_inst_ptr, tile_id, block_id, &threshold_settings) + != XRFDC_SUCCESS) { + return false; + } + threshold_settings.UpdateThreshold = threshold_id; + + // Index 0 and 1 of the threshold settings struct correspond to threshold 0 and 1. + if (threshold_id == THRESHOLD_0 || threshold_id == THRESHOLD_BOTH) { + threshold_settings.ThresholdMode[0] = mode; + threshold_settings.ThresholdAvgVal[0] = average_val; + threshold_settings.ThresholdUnderVal[0] = under_val; + threshold_settings.ThresholdOverVal[0] = over_val; + } + if (threshold_id == THRESHOLD_1 || threshold_id == THRESHOLD_BOTH) { + threshold_settings.ThresholdMode[1] = mode; + threshold_settings.ThresholdAvgVal[1] = average_val; + threshold_settings.ThresholdUnderVal[1] = under_val; + threshold_settings.ThresholdOverVal[1] = over_val; + } + + return XRFdc_SetThresholdSettings( + rfdc_inst_ptr, tile_id, block_id, &threshold_settings) + == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::clear_threshold_sticky( + uint32_t tile_id, uint32_t block_id, threshold_id_options threshold_id) +{ + bool result; + threshold_clr_mode_options old_clear_mode_0 = THRESHOLD_CLRMD_UNKNOWN, + old_clear_mode_1 = THRESHOLD_CLRMD_UNKNOWN; + + // Check current threshold clear mode + old_clear_mode_0 = get_threshold_clr_mode(tile_id, block_id, THRESHOLD_0); + old_clear_mode_1 = get_threshold_clr_mode(tile_id, block_id, THRESHOLD_1); + + // Set the clear mode to manual + if (!set_threshold_clr_mode( + tile_id, block_id, threshold_id, THRESHOLD_CLRMD_MANUAL)) { + return false; + } + + // Clear the sticky + // Do not return on a failure as the clear mode still needs to be returned to the + // previous value. + result = (XRFdc_ThresholdStickyClear(rfdc_inst_ptr, tile_id, block_id, threshold_id) + == XRFDC_SUCCESS); + + // Set the threshold clear mode back to the original setting + // If the old setting is the same or UNKNOWN this will do nothing. + result = result + && set_threshold_clr_mode(tile_id, block_id, THRESHOLD_0, old_clear_mode_0); + result = result + && set_threshold_clr_mode(tile_id, block_id, THRESHOLD_1, old_clear_mode_1); + return result; +} + +bool rfdc_ctrl::set_threshold_clr_mode(uint32_t tile_id, + uint32_t block_id, + threshold_id_options threshold_id, + threshold_clr_mode_options clear_mode) +{ + bool result; + bool mode_matches = false; + uint32_t old_clear_mode_0 = THRESHOLD_CLRMD_UNKNOWN, + old_clear_mode_1 = THRESHOLD_CLRMD_UNKNOWN; + + if ((tile_id > XRFDC_TILE_ID_MAX) || (block_id > XRFDC_BLOCK_ID_MAX)) { + return false; + } + // Do not change the clear mode to UNKNOWN + if (clear_mode == THRESHOLD_CLRMD_UNKNOWN) { + return false; + } + + // Check current threshold clear mode + switch (threshold_id) { + case THRESHOLD_0: + old_clear_mode_0 = get_threshold_clr_mode(tile_id, block_id, THRESHOLD_0); + mode_matches = (old_clear_mode_0 == clear_mode); + break; + case THRESHOLD_1: + old_clear_mode_1 = get_threshold_clr_mode(tile_id, block_id, THRESHOLD_1); + mode_matches = (old_clear_mode_1 == clear_mode); + break; + case THRESHOLD_BOTH: + old_clear_mode_0 = get_threshold_clr_mode(tile_id, block_id, THRESHOLD_0); + old_clear_mode_1 = get_threshold_clr_mode(tile_id, block_id, THRESHOLD_1); + mode_matches = + ((old_clear_mode_0 == clear_mode) && (old_clear_mode_1 == clear_mode)); + break; + } + // Do not change the clear mode if the new value matches the existing value + if (mode_matches) { + return true; + } + + result = (XRFdc_SetThresholdClrMode( + rfdc_inst_ptr, tile_id, block_id, threshold_id, clear_mode) + == XRFDC_SUCCESS); + // If the setting was not successful, save the clear mode as unknown + if (!result) { + clear_mode = THRESHOLD_CLRMD_UNKNOWN; + } + + // Set the new threshold clear mode + switch (threshold_id) { + case THRESHOLD_0: + threshold_clr_modes[tile_id][block_id][0] = clear_mode; + break; + case THRESHOLD_1: + threshold_clr_modes[tile_id][block_id][1] = clear_mode; + break; + case THRESHOLD_BOTH: + threshold_clr_modes[tile_id][block_id][0] = clear_mode; + threshold_clr_modes[tile_id][block_id][1] = clear_mode; + break; + } + return result; +} + +rfdc_ctrl::threshold_clr_mode_options rfdc_ctrl::get_threshold_clr_mode( + uint32_t tile_id, uint32_t block_id, threshold_id_options threshold_id) +{ + int threshold_index; + + // The XRFdc Threshold ID values (1-2) do not match the array indexes (0-1) + if (threshold_id == THRESHOLD_0) { + threshold_index = 0; + } else if (threshold_id == THRESHOLD_1) { + threshold_index = 1; + } + // An invalid Threshold ID was given + else { + return THRESHOLD_CLRMD_UNKNOWN; + } + if ((tile_id > XRFDC_TILE_ID_MAX) || (block_id > XRFDC_BLOCK_ID_MAX) + || (threshold_index >= THRESHOLDS_PER_BLOCK)) { + return THRESHOLD_CLRMD_UNKNOWN; + } + + return threshold_clr_modes[tile_id][block_id][threshold_index]; +} + +bool rfdc_ctrl::set_decoder_mode( + uint32_t tile_id, uint32_t block_id, decoder_mode_options decoder_mode) +{ + return XRFdc_SetDecoderMode(rfdc_inst_ptr, tile_id, block_id, decoder_mode) + == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::reset_nco_phase(uint32_t tile_id, uint32_t block_id, bool is_dac) +{ + XRFdc_Mixer_Settings mixer_settings; + + // Get current mixer settings for the values that will not be changed + if (XRFdc_GetMixerSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + != XRFDC_SUCCESS) { + return false; + } + + // Reset the phase on a SYSREF trigger + mixer_settings.EventSource = XRFDC_EVNT_SRC_SYSREF; + + // Set the mixer settings to set the NCO event source + if (XRFdc_SetMixerSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + != XRFDC_SUCCESS) { + return false; + } + + return (XRFdc_ResetNCOPhase(rfdc_inst_ptr, is_dac, tile_id, block_id) + == XRFDC_SUCCESS); +} + +bool rfdc_ctrl::set_nco_freq( + uint32_t tile_id, uint32_t block_id, bool is_dac, double freq) +{ + XRFdc_Mixer_Settings mixer_settings; + + // Get current mixer settings for the values that will not be changed + if (XRFdc_GetMixerSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + != XRFDC_SUCCESS) { + return false; + } + + // The XRFdc API expects the NCO frequency in MHz + mixer_settings.Freq = freq / 1e6; + // Only the fine mixer uses an NCO to shift the data frequency + mixer_settings.MixerType = XRFDC_MIXER_TYPE_FINE; + // Update the setting on a tile-wide event trigger + mixer_settings.EventSource = XRFDC_EVNT_SRC_TILE; + + return (XRFdc_SetMixerSettings( + rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + == XRFDC_SUCCESS) + && trigger_update_event(tile_id, block_id, is_dac, MIXER_EVENT); +} + +double rfdc_ctrl::get_nco_freq(uint32_t tile_id, uint32_t block_id, bool is_dac) +{ + XRFdc_Mixer_Settings mixer_settings; + + if (XRFdc_GetMixerSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + != XRFDC_SUCCESS) { + throw mpm::runtime_error("Error in RFDC code: Failed to get mixer settings"); + } + // The XRFdc API returns the frequency in MHz + return mixer_settings.Freq * 1e6; +} + +bool rfdc_ctrl::set_nco_event_src(uint32_t tile_id, uint32_t block_id, bool is_dac) +{ + XRFdc_Mixer_Settings mixer_settings; + + // Get current mixer settings for the values that will not be changed + if (XRFdc_GetMixerSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + != XRFDC_SUCCESS) { + return false; + } + + // Reset the phase on a SYSREF trigger + mixer_settings.EventSource = XRFDC_EVNT_SRC_SYSREF; + + // Set the mixer settings to set the NCO event source + return (XRFdc_SetMixerSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + == XRFDC_SUCCESS); +} + +bool rfdc_ctrl::set_mixer_mode( + uint32_t tile_id, uint32_t block_id, bool is_dac, mixer_mode_options mixer_mode) +{ + XRFdc_Mixer_Settings mixer_settings; + + // Get current mixer settings for the values that will not be changed + if (XRFdc_GetMixerSettings(rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + != XRFDC_SUCCESS) { + return false; + } + + mixer_settings.MixerMode = mixer_mode; + // Update the setting on a tile-wide event trigger + mixer_settings.EventSource = XRFDC_EVNT_SRC_TILE; + + return (XRFdc_SetMixerSettings( + rfdc_inst_ptr, is_dac, tile_id, block_id, &mixer_settings) + == XRFDC_SUCCESS) + && trigger_update_event(tile_id, block_id, is_dac, MIXER_EVENT); +} + +bool rfdc_ctrl::set_nyquist_zone( + uint32_t tile_id, uint32_t block_id, bool is_dac, nyquist_zone_options nyquist_zone) +{ + return XRFdc_SetNyquistZone(rfdc_inst_ptr, is_dac, tile_id, block_id, nyquist_zone) + == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::set_calibration_mode( + uint32_t tile_id, uint32_t block_id, calibration_mode_options calibration_mode) +{ + return XRFdc_SetCalibrationMode(rfdc_inst_ptr, tile_id, block_id, calibration_mode) + == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::enable_inverse_sinc_filter( + uint32_t tile_id, uint32_t block_id, bool enable) +{ + return XRFdc_SetInvSincFIR(rfdc_inst_ptr, tile_id, block_id, enable) == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::set_sample_rate(uint32_t tile_id, bool is_dac, double sample_rate) +{ + // The XRFdc API expects the sample rate in MHz + double sample_rate_mhz = sample_rate / 1e6; + return XRFdc_DynamicPLLConfig(rfdc_inst_ptr, + is_dac, + tile_id, + XRFDC_EXTERNAL_CLK, + sample_rate_mhz, + sample_rate_mhz) + == XRFDC_SUCCESS; +} + +double rfdc_ctrl::get_sample_rate(uint32_t tile_id, uint32_t block_id, bool is_dac) +{ + XRFdc_BlockStatus block_status; + + if (XRFdc_GetBlockStatus(rfdc_inst_ptr, is_dac, tile_id, block_id, &block_status) + != XRFDC_SUCCESS) { + throw mpm::runtime_error("Error in RFDC code: Failed to get Block status"); + } + // The XRFdc API returns the sampling frequency in GHz + return block_status.SamplingFreq * 1e9; +} + +bool rfdc_ctrl::set_if(uint32_t tile_id, uint32_t block_id, bool is_dac, double if_freq) +{ + nyquist_zone_options nyquist_zone; + mixer_mode_options mixer_mode; + bool enable_inverse_sinc; + double nco_freq; + + double nyquist_cutoff = get_sample_rate(tile_id, block_id, is_dac) / 2; + + if (if_freq <= nyquist_cutoff) { // First Nyquist Zone + nyquist_zone = ODD_NYQUIST_ZONE; + mixer_mode = is_dac ? MIXER_MODE_C2R : MIXER_MODE_R2C; + enable_inverse_sinc = true; + } else { // Second Nyquist Zone + nyquist_zone = EVEN_NYQUIST_ZONE; + mixer_mode = is_dac ? MIXER_MODE_C2R : MIXER_MODE_R2C; + enable_inverse_sinc = false; + } + + return set_nyquist_zone(tile_id, block_id, is_dac, nyquist_zone) + && set_mixer_mode(tile_id, block_id, is_dac, mixer_mode) + && (is_dac ? enable_inverse_sinc_filter(tile_id, block_id, enable_inverse_sinc) + : true) + && set_nco_freq(tile_id, block_id, is_dac, if_freq) + && set_nco_event_src(tile_id, block_id, is_dac); +} + +bool rfdc_ctrl::set_decimation_factor( + uint32_t tile_id, uint32_t block_id, interp_decim_options decimation_factor) +{ + return XRFdc_SetDecimationFactor(rfdc_inst_ptr, tile_id, block_id, decimation_factor) + == XRFDC_SUCCESS; +} + +rfdc_ctrl::interp_decim_options rfdc_ctrl::get_decimation_factor( + uint32_t tile_id, uint32_t block_id) +{ + uint32_t decimation_factor; + if (XRFdc_GetDecimationFactor(rfdc_inst_ptr, tile_id, block_id, &decimation_factor) + != XRFDC_SUCCESS) { + throw mpm::runtime_error("Error in RFDC code: Failed to get decimation factor"); + } + return (interp_decim_options)decimation_factor; +} + +bool rfdc_ctrl::set_interpolation_factor( + uint32_t tile_id, uint32_t block_id, interp_decim_options interpolation_factor) +{ + return XRFdc_SetInterpolationFactor( + rfdc_inst_ptr, tile_id, block_id, interpolation_factor) + == XRFDC_SUCCESS; +} + +rfdc_ctrl::interp_decim_options rfdc_ctrl::get_interpolation_factor( + uint32_t tile_id, uint32_t block_id) +{ + uint32_t interpolation_factor; + if (XRFdc_GetInterpolationFactor( + rfdc_inst_ptr, tile_id, block_id, &interpolation_factor) + != XRFDC_SUCCESS) { + throw mpm::runtime_error( + "Error in RFDC code: Failed to get interpolation factor"); + } + return (interp_decim_options)interpolation_factor; +} + +bool rfdc_ctrl::set_data_read_rate( + uint32_t tile_id, uint32_t block_id, uint32_t valid_read_words) +{ + return XRFdc_SetFabRdVldWords(rfdc_inst_ptr, tile_id, block_id, valid_read_words) + == XRFDC_SUCCESS; +} + +uint32_t rfdc_ctrl::get_data_read_rate(uint32_t tile_id, uint32_t block_id, bool is_dac) +{ + uint32_t valid_read_words; + if (XRFdc_GetFabRdVldWords( + rfdc_inst_ptr, is_dac, tile_id, block_id, &valid_read_words) + != XRFDC_SUCCESS) { + throw mpm::runtime_error("Error in RFDC code: Failed to get data read rate"); + } + return valid_read_words; +} + +bool rfdc_ctrl::set_data_write_rate( + uint32_t tile_id, uint32_t block_id, uint32_t valid_write_words) +{ + return XRFdc_SetFabWrVldWords(rfdc_inst_ptr, tile_id, block_id, valid_write_words) + == XRFDC_SUCCESS; +} + +uint32_t rfdc_ctrl::get_data_write_rate(uint32_t tile_id, uint32_t block_id, bool is_dac) +{ + uint32_t valid_write_words; + if (XRFdc_GetFabWrVldWords( + rfdc_inst_ptr, is_dac, tile_id, block_id, &valid_write_words) + != XRFDC_SUCCESS) { + throw mpm::runtime_error("Error in RFDC code: Failed to get data write rate"); + } + return valid_write_words; +} + +bool rfdc_ctrl::set_fabric_clk_div( + uint32_t tile_id, bool is_dac, fabric_clk_div_options divider) +{ + return XRFdc_SetFabClkOutDiv(rfdc_inst_ptr, is_dac, tile_id, divider) + == XRFDC_SUCCESS; +} + +rfdc_ctrl::fabric_clk_div_options rfdc_ctrl::get_fabric_clk_div( + uint32_t tile_id, bool is_dac) +{ + uint16_t divider; + if (XRFdc_GetFabClkOutDiv(rfdc_inst_ptr, is_dac, tile_id, ÷r) + != XRFDC_SUCCESS) { + throw mpm::runtime_error( + "Error in RFDC code: Failed to get fabric clock divider"); + } + return (fabric_clk_div_options)divider; +} + +bool rfdc_ctrl::set_data_fifo_state(uint32_t tile_id, bool is_dac, bool enable) +{ + return XRFdc_SetupFIFO(rfdc_inst_ptr, is_dac, tile_id, enable) == XRFDC_SUCCESS; +} + +bool rfdc_ctrl::get_data_fifo_state(uint32_t tile_id, bool is_dac) +{ + uint8_t enabled; + if (XRFdc_GetFIFOStatus(rfdc_inst_ptr, is_dac, tile_id, &enabled) != XRFDC_SUCCESS) { + throw mpm::runtime_error("Error in RFDC code: Failed to get FIFO status"); + } + return (bool)enabled; +} + +void rfdc_ctrl::clear_data_fifo_interrupts( + const uint32_t tile_id, const uint32_t block_id, const bool is_dac) +{ + if (XRFdc_IntrClr(rfdc_inst_ptr, + static_cast<u32>(is_dac), + tile_id, + block_id, + XRFDC_IXR_FIFOUSRDAT_MASK) + != XRFDC_SUCCESS) { + throw mpm::runtime_error( + "Error in RFDC code: Failed to clear data FIFO interrupts"); + } +} + +bool rfdc_ctrl::sync_tiles(const std::vector<uint32_t>& tiles, bool is_dac, uint32_t latency) +{ + XRFdc_MultiConverter_Sync_Config* sync_config = is_dac ? &rfdc_dac_sync_config + : &rfdc_adc_sync_config; + sync_config->Tiles = 0; + sync_config->Target_Latency = latency; + + for (auto tile = tiles.begin(); tile != tiles.end(); ++tile) { + // sync_config->Tiles is a bitmask, we need to "bump" each bit (0->1) + // that corresponds to the specified indices + sync_config->Tiles |= (1 << *tile); + } + + return XRFDC_MTS_OK + == XRFdc_MultiConverter_Sync( + &rfdc_inst, is_dac ? XRFDC_DAC_TILE : XRFDC_ADC_TILE, sync_config); +} + +uint32_t rfdc_ctrl::get_tile_latency(uint32_t tile_index, bool is_dac) +{ + XRFdc_MultiConverter_Sync_Config* sync_config = is_dac ? &rfdc_dac_sync_config + : &rfdc_adc_sync_config; + // If user has called sync with this tile_index, this + // attribute should be populated in our sync config + if ((1 << tile_index) & sync_config->Tiles) { + return sync_config->Latency[tile_index]; + } + if (is_dac) { + throw mpm::runtime_error("rfdc_ctrl: Failed to get DAC Tile Latency"); + } else { + throw mpm::runtime_error("rfdc_ctrl: Failed to get ADC Tile Latency"); + } +} + +uint32_t rfdc_ctrl::get_tile_offset(uint32_t tile_index, bool is_dac) +{ + XRFdc_MultiConverter_Sync_Config* sync_config = is_dac ? &rfdc_dac_sync_config + : &rfdc_adc_sync_config; + // If user has called sync with this tile_index, this + // attribute should be populated in our sync config + if ((1 << tile_index) & sync_config->Tiles) { + return sync_config->Offset[tile_index]; + } + if (is_dac) { + throw mpm::runtime_error("rfdc_ctrl: Failed to get DAC Tile Offset"); + } else { + throw mpm::runtime_error("rfdc_ctrl: Failed to get ADC Tile Offset"); + } +} + +void rfdc_ctrl::set_cal_frozen( + const uint32_t tile_id, const uint32_t block_id, const bool frozen) +{ + XRFdc_Cal_Freeze_Settings cal_freeze_settings; + cal_freeze_settings.CalFrozen = false; + cal_freeze_settings.DisableFreezePin = true; + cal_freeze_settings.FreezeCalibration = frozen; + if (XRFdc_SetCalFreeze(&rfdc_inst, tile_id, block_id, &cal_freeze_settings) + != XRFDC_SUCCESS) { + throw mpm::runtime_error( + "Error in RFDC code: Failed to set calibration freeze status"); + } +} + +bool rfdc_ctrl::get_cal_frozen(const uint32_t tile_id, const uint32_t block_id) +{ + XRFdc_Cal_Freeze_Settings cal_freeze_settings; + if (XRFdc_GetCalFreeze(&rfdc_inst, tile_id, block_id, &cal_freeze_settings) + != XRFDC_SUCCESS) { + throw mpm::runtime_error( + "Error in RFDC code: Failed to get calibration freeze status"); + } + return cal_freeze_settings.CalFrozen; +} + +void rfdc_ctrl::set_adc_cal_coefficients(uint32_t tile_id, uint32_t block_id, uint32_t cal_block, std::vector<uint32_t> coefs) +{ + if (coefs.size() != 8) + { + throw mpm::runtime_error("set_adc_cal_coefficients requires that exactly 8 coefficients be passed"); + } + + XRFdc_Calibration_Coefficients cs; + cs.Coeff0 = coefs[0]; + cs.Coeff1 = coefs[1]; + cs.Coeff2 = coefs[2]; + cs.Coeff3 = coefs[3]; + cs.Coeff4 = coefs[4]; + cs.Coeff5 = coefs[5]; + cs.Coeff6 = coefs[6]; + cs.Coeff7 = coefs[7]; + + if (XRFdc_SetCalCoefficients(&rfdc_inst, tile_id, block_id, cal_block, &cs) != XRFDC_SUCCESS) { + throw mpm::runtime_error("Error returned from XRFdc_SetCalCoefficients"); + } +} + +std::vector<uint32_t> rfdc_ctrl::get_adc_cal_coefficients(uint32_t tile_id, uint32_t block_id, uint32_t cal_block) +{ + std::vector<uint32_t> result; + XRFdc_Calibration_Coefficients cs; + if (XRFdc_GetCalCoefficients(&rfdc_inst, tile_id, block_id, cal_block, &cs) != XRFDC_SUCCESS) { + throw mpm::runtime_error("Error returned from XRFdc_GetCalCoefficients"); + } + + result.push_back(cs.Coeff0); + result.push_back(cs.Coeff1); + result.push_back(cs.Coeff2); + result.push_back(cs.Coeff3); + result.push_back(cs.Coeff4); + result.push_back(cs.Coeff5); + result.push_back(cs.Coeff6); + result.push_back(cs.Coeff7); + + return result; +} + +}} // namespace mpm::rfdc diff --git a/mpm/lib/rfdc/rfdc_throw.cpp b/mpm/lib/rfdc/rfdc_throw.cpp new file mode 100644 index 000000000..a50fe7c96 --- /dev/null +++ b/mpm/lib/rfdc/rfdc_throw.cpp @@ -0,0 +1,24 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +extern "C" { +#include "mpm/rfdc/rfdc_throw.h" +} +#include <mpm/exception.hpp> +#include <string> + +/** + * A function to throw MPM exceptions from within the Xilinx RFdc API + */ +void rfdc_throw(const char* msg) +{ + if (msg) { + std::string error_msg(msg); + throw mpm::assertion_error("Error in RFDC code: " + error_msg); + } else { + throw mpm::assertion_error("Error in RFDC code."); + } +} diff --git a/mpm/lib/rfdc/xrfdc.c b/mpm/lib/rfdc/xrfdc.c new file mode 100644 index 000000000..6261db774 --- /dev/null +++ b/mpm/lib/rfdc/xrfdc.c @@ -0,0 +1,5345 @@ +/****************************************************************************** +* +* Copyright (C) 2017-2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc.c +* @addtogroup xrfdc_v6_0 +* @{ +* +* Contains the interface functions of the XRFdc driver. +* See xrfdc.h for a detailed description of the device and driver. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.0 sk 05/16/17 Initial release +* 2.0 sk 08/09/17 Fixed coarse Mixer configuration settings +* CR# 977266, 977872. +* Return error for Slice Event on 4G ADC Block. +* 08/16/17 Add support for SYSREF and PL event sources. +* 08/18/17 Add API to enable and disable FIFO. +* 08/23/17 Add API to configure Nyquist zone. +* 08/30/17 Add additional info to BlockStatus. +* 08/30/17 Add support for Coarse Mixer BYPASS mode. +* 08/31/17 Removed Tile Reset Assert and Deassert. +* 09/07/17 Add support for negative NCO freq. +* 09/15/17 Fixed NCO freq precision issue. +* 09/15/17 Fixed Immediate Event source issue and also +* updated the Immediate Macro value to 0. +* 2.1 sk 09/15/17 Remove Libmetal library dependency for MB. +* sk 09/25/17 Modified XRFdc_GetBlockStatus API to give +* correct information and also updates the +* description for Vector Param in intr handler +* Add API to get Output current and removed +* GetTermVoltage and GetOutputCurr inline functions. +* 2.2 sk 10/05/17 Fixed XRFdc_GetNoOfADCBlocks API for 4GSPS. +* Enable the decoder clock based on decoder mode. +* Add API to get the current FIFO status. +* Updated XRFdc_DumpRegs API for better readability +* of output register dump. +* Add support for 4GSPS CoarseMixer frequency. +* 10/11/17 Modify float types to double to increase precision. +* 10/12/17 Update BlockStatus API to give current status. +* In BYPASS mode, input datatype can be Real or IQ, +* hence checked both while reading the mixer mode. +* 10/17/17 Fixed Set Threshold API Issue. +* 2.3 sk 11/06/17 Fixed PhaseOffset truncation issue. +* Provide user configurability for FineMixerScale. +* 11/08/17 Return error for DAC R2C mode and ADC C2R mode. +* 11/20/17 Fixed StartUp, Shutdown and Reset API for Tile_Id -1. +* 11/20/17 Remove unwanted ADC block checks in 4GSPS mode. +* 3.0 sk 12/11/17 Added DDC and DUC support. +* 12/13/17 Add CoarseMixMode field in Mixer_Settings structure. +* 12/15/17 Add support to switch calibration modes. +* 12/15/17 Add support for mixer frequencies > Fs/2 and < -Fs/2. +* sg 13/01/18 Added PLL and external clock switch support. +* Added API to get PLL lock status. +* Added API to get clock source. +* 3.1 jm 01/24/18 Add Multi-tile sync support. +* sk 01/25/18 Updated Set and Get Interpolation/Decimation factor +* API's to consider the actual factor value. +* 3.2 sk 02/02/18 Add API's to configure inverse-sinc. +* sk 02/27/18 Add API's to configure Multiband. +* sk 03/09/18 Update PLL structure in XRFdc_DynamicPLLConfig API. +* sk 03/09/18 Update ADC and DAC datatypes in Mixer API and use +* input datatype for ADC in threshold and QMC APIs. +* sk 03/09/18 Removed FIFO disable check in DDC and DUC APIs. +* sk 03/09/18 Add support for Marker event source for DAC block. +* sk 03/22/18 Updated PLL settings based on latest IP values. +* 4.0 sk 04/17/18 Corrected Set/Get MixerSettings API description for +* FineMixerScale parameter. +* sk 04/19/18 Enable VCO Auto selection while configuring the clock. +* sk 04/24/18 Add API to get PLL Configurations. +* sk 04/24/18 Add API to get the Link Coupling mode. +* sk 04/28/18 Implement timeouts for PLL Lock, Startup and shutdown. +* sk 05/30/18 Removed CalibrationMode check for DAC. +* sk 06/05/18 Updated minimum Ref clock value to 102.40625MHz. +* 5.0 sk 06/25/18 Update DAC min sampling rate to 500MHz and also update +* VCO Range, PLL_DIVIDER and PLL_FPDIV ranges. +* sk 06/25/18 Add XRFdc_GetFabClkOutDiv() API to read fabric clk div. +* Add Inline APIs XRFdc_CheckBlockEnabled(), +* XRFdc_CheckTileEnabled(). +* sk 07/06/18 Add support to dump HSCOM regs in XRFdc_DumpRegs() API +* sk 07/12/18 Fixed Multiband crossbar settings in C2C mode. +* sk 07/19/18 Add MixerType member to MixerSettings structure and +* Update Mixer Settings APIs to consider the MixerType +* variable. +* sk 07/19/18 Add XRFdc_GetMultibandConfig() API to read Multiband +* configuration. +* sk 07/20/18 Update the APIs to check the corresponding section +* (Digital/Analog)enable/disable. +* sk 07/26/18 Fixed Doxygen, coverity warnings. +* sk 08/03/18 Fixed MISRAC warnings. +* sk 08/24/18 Move mixer related APIs to xrfdc_mixer.c file. +* Define asserts for Linux, Re-arranged XRFdc_RestartIPSM, +* XRFdc_CfgInitialize() and XRFdc_MultiBand() APIs. +* Reorganize the code to improve readability and +* optimization. +* sk 09/24/18 Update powerup-state value based on PLL mode in +* XRFdc_DynamicPLLConfig() API. +* sk 10/10/18 Check for DigitalPath enable in XRFdc_GetNyquistZone() +* and XRFdc_GetCalibrationMode() APIs for Multiband. +* sk 10/13/18 Add support to read the REFCLKDIV param from design. +* Update XRFdc_SetPLLConfig() API to support range of +* REF_CLK_DIV values(1 to 4). +* 5.1 cog 01/29/19 Replace structure reference ADC checks with +* function. +* cog 01/29/19 Added XRFdc_SetDither() and XRFdc_GetDither() APIs. +* cog 01/29/19 Rename DataType for mixer input to MixerInputDataType +* for readability. +* cog 01/29/19 Refactoring of interpolation and decimation APIs and +* changed fabric rate for decimation X8 for non-high speed ADCs. +* cog 01/29/19 New inline functions to determine max & min sampling rates +* rates in PLL range checking. +* 6.0 cog 02/17/19 Added decimation & interpolation modes +* 02/17/19 Added Inverse-Sinc Second Nyquist Zone Support +* cog 02/17/19 Added new clock Distribution functionality. +* cog 02/17/19 Refactored to improve delay balancing in clock +* distribution. +* cog 02/17/19 Added delay calculation & metal log messages. +* cog 02/17/19 Added intratile clock settings. +* cog 02/17/19 Moved multiband to a new file xrfdc_mb.c +* cog 02/17/19 Moved clocking functionality to a new file xrfdc_clock.c +* cog 02/17/19 Added XRFdc_SetIMRPassMode() and XRFdc_SetIMRPassMode() APIs +* cog 02/17/19 Added XRFdc_SetDACMode() and XRFdc_GetDACMode() APIs +* cog 02/17/19 Added XRFdc_SetSignalDetector() and XRFdc_GetSignalDetector() APIs. +* cog 02/17/19 Added XRFdc_DisableCoefficientsOverride(), XRFdc_SetCalCoefficients +* and XRFdc_GetCalCoefficients APIs. +* cog 02/21/19 Added XRFdc_SetCalFreeze() and XRFdc_GetCalFreeze() APIs. +* cog 04/09/19 Changed Calibrtation coefficient override control register for OCB1. +* cog 04/15/19 Rename XRFdc_SetDACMode() and XRFdc_GetDACMode() APIs to +* XRFdc_SetDataPathMode() and XRFdc_GetDataPathMode() respectively. +* cog 04/30/19 Made Changes to the bypass calibration functionality to support Gen2 +* and below. +* +* </pre> +* +******************************************************************************/ + +/***************************** Include Files *********************************/ +#include "mpm/rfdc/xrfdc.h" + +/************************** Constant Definitions *****************************/ +#define XRFDC_PLL_LOCK_DLY_CNT 1000U + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static u32 XRFdc_RestartIPSM(XRFdc *InstancePtr, u32 Type, int Tile_Id, + u32 Start, u32 End); +static void StubHandler(void *CallBackRefPtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 StatusEvent); +static void XRFdc_ADCInitialize(XRFdc *InstancePtr); +static void XRFdc_DACInitialize(XRFdc *InstancePtr); +static void XRFdc_DACMBConfigInit(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id); +static void XRFdc_ADCMBConfigInit(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id); +static void XRFdc_UpdatePLLStruct(XRFdc *InstancePtr, u32 Type, u32 Tile_Id); +static u32 XRFdc_GetADCBlockStatus(XRFdc *InstancePtr, u32 BaseAddr, + u32 Tile_Id, u32 Block_Id, XRFdc_BlockStatus *BlockStatusPtr); +static u32 XRFdc_GetDACBlockStatus(XRFdc *InstancePtr, u32 BaseAddr, + u32 Tile_Id, u32 Block_Id, XRFdc_BlockStatus *BlockStatusPtr); +static void XRFdc_DumpHSCOMRegs(XRFdc *InstancePtr, u32 Type, int Tile_Id); +static void XRFdc_DumpDACRegs(XRFdc *InstancePtr, int Tile_Id); +static void XRFdc_DumpADCRegs(XRFdc *InstancePtr, int Tile_Id); +static u32 XRFdc_WaitForRestartClr(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 BaseAddr, u32 End); + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** +* +* Initializes a specific XRFdc instance such that the driver is ready to use. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param ConfigPtr is a reference to a structure containing information +* about xrfdc. This function initializes an InstancePtr object +* for a specific device specified by the contents of Config. +* +* @return +* - XRFDC_SUCCESS if successful. +* +* @note The user needs to first call the XRFdc_LookupConfig() API +* which returns the Configuration structure pointer which is +* passed as a parameter to the XRFdc_CfgInitialize() API. +* +******************************************************************************/ +u32 XRFdc_CfgInitialize(XRFdc *InstancePtr, XRFdc_Config *ConfigPtr) +{ + u32 Status; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(ConfigPtr != NULL); + + InstancePtr->io = (struct metal_io_region *) + metal_allocate_memory(sizeof(struct metal_io_region)); + metal_io_init(InstancePtr->io, (void *)(metal_phys_addr_t)ConfigPtr->BaseAddr, + &ConfigPtr->BaseAddr, XRFDC_REGION_SIZE, (unsigned)(-1), 0, NULL); + + /* + * Set the values read from the device config and the base address. + */ + InstancePtr->BaseAddr = ConfigPtr->BaseAddr; + InstancePtr->RFdc_Config = *ConfigPtr; + InstancePtr->ADC4GSPS = ConfigPtr->ADCType; + InstancePtr->StatusHandler = StubHandler; + + /* Initialize ADC */ + XRFdc_ADCInitialize(InstancePtr); + + /* Initialize DAC */ + XRFdc_DACInitialize(InstancePtr); + + /* + * Indicate the instance is now ready to use and + * initialized without error. + */ + InstancePtr->IsReady = XRFDC_COMPONENT_IS_READY; + + Status = XRFDC_SUCCESS; + return Status; +} + +/*****************************************************************************/ +/** +* +* Initialize ADC Tiles. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* +* @return +* - None. +* +* @note Static API used to initialize ADC Tiles +* +******************************************************************************/ +static void XRFdc_ADCInitialize(XRFdc *InstancePtr) +{ + u32 Tile_Id; + u32 Block_Id; + u8 MixerType; + + for (Tile_Id = XRFDC_TILE_ID0; Tile_Id < XRFDC_TILE_ID4; Tile_Id++) { + InstancePtr->ADC_Tile[Tile_Id].NumOfADCBlocks = 0U; + for (Block_Id = XRFDC_BLK_ID0; Block_Id < XRFDC_BLK_ID4; Block_Id++) { + if (XRFdc_IsADCBlockEnabled(InstancePtr, Tile_Id, Block_Id) != 0U) { + InstancePtr->ADC_Tile[Tile_Id].NumOfADCBlocks += 1U; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Analog_Datapath[Block_Id]. + AnalogPathEnabled = XRFDC_ANALOGPATH_ENABLE; + } + /* Initialize Data Type */ + if (InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id]. + ADCBlock_Analog_Config[Block_Id].MixMode == XRFDC_MIXER_MODE_BYPASS) { + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Block_Id].MixerInputDataType = + InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].ADCBlock_Digital_Config[Block_Id].MixerInputDataType; + } else { + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Block_Id].MixerInputDataType = + InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id]. + ADCBlock_Analog_Config[Block_Id].MixMode; + } + /* Initialize MixerType */ + MixerType = InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id]. + ADCBlock_Digital_Config[Block_Id].MixerType; + InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].Mixer_Settings.MixerType = MixerType; + + InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].ConnectedIData = -1; + InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].ConnectedQData = -1; + InstancePtr->ADC_Tile[Tile_Id].MultibandConfig = + InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].MultibandConfig; + if (XRFdc_IsADCDigitalPathEnabled(InstancePtr, Tile_Id, Block_Id) != 0U) { + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Block_Id].DigitalPathAvailable = + XRFDC_DIGITALPATH_ENABLE; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Block_Id].DigitalPathEnabled = + XRFDC_DIGITALPATH_ENABLE; + /* Initialize ConnectedI/QData, MB Config */ + XRFdc_ADCMBConfigInit(InstancePtr, Tile_Id, Block_Id); + } + } + + /* Initialize PLL Structure */ + XRFdc_UpdatePLLStruct(InstancePtr, XRFDC_ADC_TILE, Tile_Id); + } +} + +/*****************************************************************************/ +/** +* +* Initialize DAC Tiles. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* +* @return +* - None. +* +* @note Static API used to initialize DAC Tiles +* +******************************************************************************/ +static void XRFdc_DACInitialize(XRFdc *InstancePtr) +{ + u32 Tile_Id; + u32 Block_Id; + u8 MixerType; + + for (Tile_Id = XRFDC_TILE_ID0; Tile_Id < XRFDC_TILE_ID4; Tile_Id++) { + InstancePtr->DAC_Tile[Tile_Id].NumOfDACBlocks = 0U; + for (Block_Id = XRFDC_BLK_ID0; Block_Id < XRFDC_BLK_ID4; Block_Id++) { + if (XRFdc_IsDACBlockEnabled(InstancePtr, Tile_Id, Block_Id) != 0U) { + InstancePtr->DAC_Tile[Tile_Id].NumOfDACBlocks += 1U; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Analog_Datapath[Block_Id].AnalogPathEnabled = + XRFDC_ANALOGPATH_ENABLE; + } + /* Initialize Data Type */ + if (InstancePtr->RFdc_Config.DACTile_Config[Tile_Id]. + DACBlock_Analog_Config[Block_Id].MixMode == XRFDC_MIXER_MODE_BYPASS) { + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[Block_Id].MixerInputDataType = + InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].DACBlock_Digital_Config[Block_Id].MixerInputDataType; + } else { + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[Block_Id].MixerInputDataType = + XRFDC_DATA_TYPE_IQ; + } + /* Initialize MixerType */ + MixerType = InstancePtr->RFdc_Config.DACTile_Config[Tile_Id]. + DACBlock_Digital_Config[Block_Id].MixerType; + InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Block_Id].Mixer_Settings.MixerType = MixerType; + + InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Block_Id].ConnectedIData = -1; + InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Block_Id].ConnectedQData = -1; + InstancePtr->DAC_Tile[Tile_Id].MultibandConfig = + InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].MultibandConfig; + if (XRFdc_IsDACDigitalPathEnabled(InstancePtr, Tile_Id, Block_Id) != 0U) { + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[Block_Id].DigitalPathAvailable = + XRFDC_DIGITALPATH_ENABLE; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[Block_Id].DigitalPathEnabled = + XRFDC_DIGITALPATH_ENABLE; + /* Initialize ConnectedI/QData, MB Config */ + XRFdc_DACMBConfigInit(InstancePtr, Tile_Id, Block_Id); + } + } + /* Initialize PLL Structure */ + XRFdc_UpdatePLLStruct(InstancePtr, XRFDC_DAC_TILE, Tile_Id); + } +} + +/*****************************************************************************/ +/** +* +* Initialize Multiband Configuration for DAC Tiles. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3, and -1. +* @param Block_Id is DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - None. +* +* @note Static API used to initialize DAC MB Config +* +******************************************************************************/ +static void XRFdc_DACMBConfigInit(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id) +{ + if (InstancePtr->RFdc_Config.DACTile_Config[Tile_Id]. + DACBlock_Analog_Config[Block_Id].MixMode == XRFDC_MIXER_MODE_C2C) { + /* Mixer Mode is C2C */ + switch (InstancePtr->DAC_Tile[Tile_Id].MultibandConfig) { + case XRFDC_MB_MODE_4X: + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID0, XRFDC_BLK_ID1); + break; + case XRFDC_MB_MODE_2X_BLK01_BLK23: + case XRFDC_MB_MODE_2X_BLK01: + case XRFDC_MB_MODE_2X_BLK23: + if ((Block_Id == XRFDC_BLK_ID0) || (Block_Id == XRFDC_BLK_ID1)) { + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID0, XRFDC_BLK_ID1); + } else { + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID2, XRFDC_BLK_ID3); + } + break; + default: + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id, Block_Id, Block_Id + 1U); + break; + } + } else if (InstancePtr->RFdc_Config.DACTile_Config[Tile_Id]. + DACBlock_Analog_Config[Block_Id].MixMode == 0x0) { + /* Mixer Mode is C2R */ + switch (InstancePtr->DAC_Tile[Tile_Id].MultibandConfig) { + case XRFDC_MB_MODE_4X: + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID0, -1); + break; + case XRFDC_MB_MODE_2X_BLK01_BLK23: + case XRFDC_MB_MODE_2X_BLK01: + case XRFDC_MB_MODE_2X_BLK23: + if ((Block_Id == XRFDC_BLK_ID0) || (Block_Id == XRFDC_BLK_ID1)) { + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID0, -1); + } else { + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID2, -1); + } + break; + default: + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id, Block_Id, -1); + break; + } + } else { + /* Mixer Mode is BYPASS */ + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id, Block_Id, -1); + } +} +/*****************************************************************************/ +/** +* +* Initialize Multiband Configuration for ADC Tiles. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3, and -1. +* @param Block_Id is ADC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - None. +* +* @note Static API used to initialize ADC MB Config +* +******************************************************************************/ +static void XRFdc_ADCMBConfigInit(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id) +{ + if (InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id]. + ADCBlock_Analog_Config[Block_Id].MixMode == XRFDC_MIXER_MODE_C2C) { + /* Mixer mode is C2C */ + switch (InstancePtr->ADC_Tile[Tile_Id].MultibandConfig) { + case XRFDC_MB_MODE_4X: + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID0, XRFDC_BLK_ID1); + break; + case XRFDC_MB_MODE_2X_BLK01_BLK23: + case XRFDC_MB_MODE_2X_BLK01: + case XRFDC_MB_MODE_2X_BLK23: + if ((Block_Id == XRFDC_BLK_ID0) || (Block_Id == XRFDC_BLK_ID1)) { + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID0, XRFDC_BLK_ID1); + } else { + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID2, XRFDC_BLK_ID3); + } + break; + default: + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id, Block_Id, Block_Id + 1U); + break; + } + } else if (InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id]. + ADCBlock_Analog_Config[Block_Id].MixMode == 0x0) { + /* Mixer mode is R2C */ + switch (InstancePtr->ADC_Tile[Tile_Id].MultibandConfig) { + case XRFDC_MB_MODE_4X: + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID0, -1); + break; + case XRFDC_MB_MODE_2X_BLK01_BLK23: + case XRFDC_MB_MODE_2X_BLK01: + case XRFDC_MB_MODE_2X_BLK23: + if ((Block_Id == XRFDC_BLK_ID0) || (Block_Id == XRFDC_BLK_ID1)) { + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID0, -1); + } else { + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id, XRFDC_BLK_ID2, -1); + } + break; + default: + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id, Block_Id, -1); + break; + } + } else { + /* Mixer mode is BYPASS */ + XRFdc_SetConnectedIQData(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id, Block_Id, -1); + } +} + +/*****************************************************************************/ +/** +* +* This API updates PLL Structure. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC. +* @param Tile_Id Valid values are 0-3, and -1. +* +* @return +* - None. +* +* @note Static API used to initialize PLL Settings for ADC and DAC +* +******************************************************************************/ +static void XRFdc_UpdatePLLStruct(XRFdc *InstancePtr, u32 Type, u32 Tile_Id) +{ + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.SampleRate = + InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].SamplingRate; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.RefClkFreq = + InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].RefClkFreq; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.Enabled = + InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].PLLEnable; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.FeedbackDivider = + InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].FeedbackDiv; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.OutputDivider = + InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].OutputDiv; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.RefClkDivider = + InstancePtr->RFdc_Config.ADCTile_Config[Tile_Id].RefClkDiv; + } else { + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.SampleRate = + InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].SamplingRate; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.RefClkFreq = + InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].RefClkFreq; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.Enabled = + InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].PLLEnable; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.FeedbackDivider = + InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].FeedbackDiv; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.OutputDivider = + InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].OutputDiv; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.RefClkDivider = + InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].RefClkDiv; + } +} + +/*****************************************************************************/ +/** +* +* The API Restarts the requested tile. It can restart a single tile and +* alternatively can restart all the tiles. Existing register settings are not +* lost or altered in the process. It just starts the requested tile(s). +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if tile is not enabled or available +* +* @note None +* +******************************************************************************/ +u32 XRFdc_StartUp(XRFdc *InstancePtr, u32 Type, int Tile_Id) +{ + u32 Status; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_RestartIPSM(InstancePtr, Type, Tile_Id, XRFDC_SM_STATE1, + XRFDC_SM_STATE15); + return Status; + +} + +/*****************************************************************************/ +/** +* +* The API stops the tile as requested. It can also stop all the tiles if +* asked for. It does not clear any of the existing register settings. It just +* stops the requested tile(s). +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if tile is not enabled or available +* +* @note None +* +******************************************************************************/ +u32 XRFdc_Shutdown(XRFdc *InstancePtr, u32 Type, int Tile_Id) +{ + u32 Status; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_RestartIPSM(InstancePtr, Type, Tile_Id, XRFDC_SM_STATE1, + XRFDC_SM_STATE1); + return Status; + +} + +/*****************************************************************************/ +/** +* +* The API resets the requested tile. It can reset all the tiles as well. In +* the process, all existing register settings are cleared and are replaced +* with the settings initially configured (through the GUI). +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if tile is not enabled or available +* +* @note None +******************************************************************************/ +u32 XRFdc_Reset(XRFdc *InstancePtr, u32 Type, int Tile_Id) +{ + u32 Status; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_RestartIPSM(InstancePtr, Type, Tile_Id, XRFDC_SM_STATE0, + XRFDC_SM_STATE15); + return Status; + +} + +/*****************************************************************************/ +/** +* +* This Static API will be used to wait for restart bit clears and also check +* for PLL Lock if clock source is internal PLL. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param End is end state of State Machine. +* +* @return - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if timeout occurs. +* +* @note None +* +******************************************************************************/ +static u32 XRFdc_WaitForRestartClr(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 BaseAddr, u32 End) +{ + u32 ClkSrc = 0U; + u32 DelayCount; + u32 LockStatus = 0U; + u32 Status; + + /* + * Get Tile clock source information + */ + if (XRFdc_GetClockSource(InstancePtr, Type, Tile_Id, &ClkSrc) + != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + if ((ClkSrc == XRFDC_INTERNAL_PLL_CLK) && (End == XRFDC_SM_STATE15)) { + /* + * Wait for internal PLL to lock + */ + if (XRFdc_GetPLLLockStatus(InstancePtr, Type, Tile_Id, + &LockStatus) != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + DelayCount = 0U; + while (LockStatus != XRFDC_PLL_LOCKED) { + if (DelayCount == XRFDC_PLL_LOCK_DLY_CNT) { + metal_log(METAL_LOG_ERROR, "\n PLL Lock timeout " + "error in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } else { + /* Wait for 1 msec */ +#ifdef __BAREMETAL__ + usleep(1000); +#else + metal_sleep_usec(1000); +#endif + DelayCount++; + (void)XRFdc_GetPLLLockStatus(InstancePtr, Type, Tile_Id, + &LockStatus); + } + } + } + + /* Wait till restart bit clear */ + DelayCount = 0U; + while (XRFdc_ReadReg16(InstancePtr, BaseAddr, XRFDC_RESTART_OFFSET) != 0U) { + if (DelayCount == XRFDC_PLL_LOCK_DLY_CNT) { + metal_log(METAL_LOG_ERROR, "\n Failed to clear " + "the restart bit in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } else { + /* Wait for 1 msec */ +#ifdef __BAREMETAL__ + usleep(1000); +#else + metal_sleep_usec(1000); +#endif + DelayCount++; + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* Restarts a requested the tile and ensures that starts from a defined start +* state and reaches the requested or defined end state. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* @param Start is start state of State Machine +* @param End is end state of State Machine. +* +* @return - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if tile is not enabled or available +* +* @note None +* +******************************************************************************/ +static u32 XRFdc_RestartIPSM(XRFdc *InstancePtr, u32 Type, int Tile_Id, + u32 Start, u32 End) +{ + u32 Status; + u32 BaseAddr; + u16 NoOfTiles; + u16 Index; + + /* An input tile if of -1 selects all tiles */ + if (Tile_Id == XRFDC_SELECT_ALL_TILES) { + NoOfTiles = XRFDC_NUM_OF_TILES4; + Index = XRFDC_TILE_ID0; + } else { + NoOfTiles = Tile_Id + 1; + Index = Tile_Id; + } + + for (; Index < NoOfTiles; Index++) { + BaseAddr = XRFDC_CTRL_STS_BASE(Type, Index); + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Index); + if ((Status != XRFDC_SUCCESS) && (Tile_Id != XRFDC_SELECT_ALL_TILES)) { + metal_log(METAL_LOG_ERROR, "\n Requested tile%d not " + "available in %s\r\n", Index, __func__); + goto RETURN_PATH; + } else if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_DEBUG, "\n Tile%d not " + "available in %s\r\n", Index, __func__); + continue; + } else { + /* Write Start and End states */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_RESTART_STATE_OFFSET, + XRFDC_PWR_STATE_MASK, (Start << XRFDC_RSR_START_SHIFT) | End); + + /* Trigger restart */ + XRFdc_WriteReg(InstancePtr, BaseAddr, XRFDC_RESTART_OFFSET, + XRFDC_RESTART_MASK); + + /* Wait for restart bit clear */ + Status = XRFdc_WaitForRestartClr(InstancePtr, Type, Index, + BaseAddr, End); + if (Status != XRFDC_SUCCESS) { + goto RETURN_PATH; + } + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* The API returns the IP status. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param IPStatusPtr Pointer to the XRFdc_IPStatus structure through +* which the status is returned. +* +* @return +* - XRFDC_SUCCESS if successful. +* +* @note None. +* +******************************************************************************/ +u32 XRFdc_GetIPStatus(XRFdc *InstancePtr, XRFdc_IPStatus *IPStatusPtr) +{ + u32 Tile_Id; + u32 Block_Id; + u32 BaseAddr; + u16 ReadReg; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(IPStatusPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + for (Tile_Id = XRFDC_TILE_ID0; Tile_Id < XRFDC_TILE_ID4; Tile_Id++) { + IPStatusPtr->ADCTileStatus[Tile_Id].BlockStatusMask = 0x0; + IPStatusPtr->DACTileStatus[Tile_Id].BlockStatusMask = 0x0; + for (Block_Id = XRFDC_BLK_ID0; Block_Id < XRFDC_BLK_ID4; Block_Id++) { + if (XRFdc_IsADCBlockEnabled(InstancePtr, Tile_Id, + Block_Id) != 0U) { + IPStatusPtr->ADCTileStatus[Tile_Id].IsEnabled = 1; + IPStatusPtr->ADCTileStatus[Tile_Id].BlockStatusMask |= + (1U << Block_Id); + BaseAddr = XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id); + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_STATUS_OFFSET); + IPStatusPtr->ADCTileStatus[Tile_Id].PowerUpState = (ReadReg & + XRFDC_PWR_UP_STAT_MASK) >> XRFDC_PWR_UP_STAT_SHIFT; + IPStatusPtr->ADCTileStatus[Tile_Id].PLLState = (ReadReg & + XRFDC_PLL_LOCKED_MASK) >> XRFDC_PLL_LOCKED_SHIFT; + IPStatusPtr->ADCTileStatus[Tile_Id].TileState = + XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_CURRENT_STATE_OFFSET); + } + if (XRFdc_IsDACBlockEnabled(InstancePtr, Tile_Id, + Block_Id) != 0U) { + IPStatusPtr->DACTileStatus[Tile_Id].IsEnabled = 1; + IPStatusPtr->DACTileStatus[Tile_Id].BlockStatusMask |= + (1U << Block_Id); + BaseAddr = XRFDC_DAC_TILE_CTRL_STATS_ADDR(Tile_Id); + IPStatusPtr->DACTileStatus[Tile_Id].TileState = + XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_CURRENT_STATE_OFFSET); + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_STATUS_OFFSET); + IPStatusPtr->DACTileStatus[Tile_Id].PowerUpState = (ReadReg & + XRFDC_PWR_UP_STAT_MASK) >> XRFDC_PWR_UP_STAT_SHIFT; + IPStatusPtr->DACTileStatus[Tile_Id].PLLState = (ReadReg & + XRFDC_PLL_LOCKED_MASK) >> XRFDC_PLL_LOCKED_SHIFT; + } + } + } + + /*TODO IP state*/ + + return XRFDC_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* The API returns the requested block status. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. XRFdc_BlockStatus. +* @param BlockStatusPtr is Pointer to the XRFdc_BlockStatus structure through +* which the ADC/DAC block status is returned. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Common API for ADC/DAC blocks. +* +******************************************************************************/ +u32 XRFdc_GetBlockStatus(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_BlockStatus *BlockStatusPtr) +{ + u32 Status; + u32 Block; + u16 ReadReg; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(BlockStatusPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + Block = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1) && (Type == XRFDC_ADC_TILE)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Block_Id); + if (Type == XRFDC_ADC_TILE) { + Status = XRFdc_GetADCBlockStatus(InstancePtr, BaseAddr, Tile_Id, + Block, BlockStatusPtr); + } else { + Status = XRFdc_GetDACBlockStatus(InstancePtr, BaseAddr, Tile_Id, + Block, BlockStatusPtr); + } + if (Status != XRFDC_SUCCESS) { + goto RETURN_PATH; + } + + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CLK_EN_OFFSET, + XRFDC_DAT_CLK_EN_MASK); + if (ReadReg == XRFDC_DAT_CLK_EN_MASK) { + BlockStatusPtr->DataPathClocksStatus = 0x1U; + } else { + BlockStatusPtr->DataPathClocksStatus = 0x0U; + } + + Status = XRFDC_SUCCESS; + +RETURN_PATH: + return Status; + +} + +/*****************************************************************************/ +/** +* +* The API returns the requested block status for ADC block +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. XRFdc_BlockStatus. +* @param BlockStatus is Pointer to the XRFdc_BlockStatus structure through +* which the ADC/DAC block status is returned. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Static API for ADC blocks. +* +******************************************************************************/ +static u32 XRFdc_GetADCBlockStatus(XRFdc *InstancePtr, u32 BaseAddr, + u32 Tile_Id, u32 Block_Id, XRFdc_BlockStatus *BlockStatusPtr) +{ + u8 FIFOEnable = 0U; + u32 DecimationFactor = 0U; + u8 MixerMode; + u16 ReadReg; + u32 Status; + + BlockStatusPtr->SamplingFreq = InstancePtr->ADC_Tile[Tile_Id]. + PLL_Settings.SampleRate; + + /* DigitalDataPathStatus */ + (void)XRFdc_GetFIFOStatus(InstancePtr, XRFDC_ADC_TILE, + Tile_Id, &FIFOEnable); + BlockStatusPtr->DigitalDataPathStatus = FIFOEnable; + (void)XRFdc_GetDecimationFactor(InstancePtr, Tile_Id, + Block_Id, &DecimationFactor); + BlockStatusPtr->DigitalDataPathStatus |= + (DecimationFactor << XRFDC_DIGI_ANALOG_SHIFT4); + + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_MXR_MODE_OFFSET, + (XRFDC_EN_I_IQ_MASK | XRFDC_EN_Q_IQ_MASK)); + switch (ReadReg) { + case XRFDC_MIXER_MODE_C2C_MASK: + MixerMode = XRFDC_MIXER_MODE_C2C; + break; + case XRFDC_MIXER_MODE_R2C_MASK: + MixerMode = XRFDC_MIXER_MODE_R2C; + break; + case XRFDC_MIXER_MODE_OFF_MASK: + MixerMode = XRFDC_MIXER_MODE_OFF; + break; + default: + metal_log(METAL_LOG_ERROR, "\n Invalid MixerMode " + "for ADC in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + BlockStatusPtr->DigitalDataPathStatus |= + (MixerMode << XRFDC_DIGI_ANALOG_SHIFT8); + + /* + * Checking ADC block enable for ADC AnalogPath. + * This can be changed later, + */ + BlockStatusPtr->AnalogDataPathStatus = + XRFdc_IsADCBlockEnabled(InstancePtr, Tile_Id, Block_Id); + BlockStatusPtr->IsFIFOFlagsEnabled = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_FABRIC_IMR_OFFSET, XRFDC_FAB_IMR_USRDAT_MASK); + BlockStatusPtr->IsFIFOFlagsAsserted = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_FABRIC_ISR_OFFSET, XRFDC_FAB_ISR_USRDAT_MASK); + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* The API returns the requested block status for DAC block +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. XRFdc_BlockStatus. +* @param BlockStatus is Pointer to the XRFdc_BlockStatus structure through +* which the DAC block status is returned. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Static API for DAC blocks. +* +******************************************************************************/ +static u32 XRFdc_GetDACBlockStatus(XRFdc *InstancePtr, u32 BaseAddr, + u32 Tile_Id, u32 Block_Id, XRFdc_BlockStatus *BlockStatusPtr) +{ + u32 InterpolationFactor = 0U; + u32 DecoderMode = 0U; + u8 MixerMode; + u16 ReadReg; + u32 Status; + u8 FIFOEnable = 0U; + + BlockStatusPtr->SamplingFreq = InstancePtr->DAC_Tile[Tile_Id]. + PLL_Settings.SampleRate; + + /* DigitalDataPathStatus */ + (void)XRFdc_GetFIFOStatus(InstancePtr, XRFDC_DAC_TILE, + Tile_Id, &FIFOEnable); + BlockStatusPtr->DigitalDataPathStatus = FIFOEnable; + (void)XRFdc_GetInterpolationFactor(InstancePtr, Tile_Id, + Block_Id, &InterpolationFactor); + BlockStatusPtr->DigitalDataPathStatus |= + (InterpolationFactor << XRFDC_DIGI_ANALOG_SHIFT4); + /* Adder Enable */ + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_MB_CFG_OFFSET, XRFDC_EN_MB_MASK); + ReadReg = ReadReg >> XRFDC_EN_MB_SHIFT; + BlockStatusPtr->DigitalDataPathStatus |= + (ReadReg << XRFDC_DIGI_ANALOG_SHIFT8); + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_MXR_MODE_OFFSET, + (XRFDC_EN_I_IQ_MASK | XRFDC_EN_Q_IQ_MASK)); + switch (ReadReg) { + case XRFDC_MIXER_MODE_C2C_MASK: + MixerMode = XRFDC_MIXER_MODE_C2C; + break; + case XRFDC_MIXER_MODE_C2R_MASK: + MixerMode = XRFDC_MIXER_MODE_C2R; + break; + case XRFDC_MIXER_MODE_OFF_MASK: + MixerMode = XRFDC_MIXER_MODE_OFF; + break; + default: + metal_log(METAL_LOG_ERROR, "\n Invalid MixerMode " + "for ADC in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + BlockStatusPtr->DigitalDataPathStatus |= + (MixerMode << XRFDC_DIGI_ANALOG_SHIFT12); + + /* AnalogDataPathStatus */ + BlockStatusPtr->AnalogDataPathStatus = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_INVSINC_OFFSET, (InstancePtr->RFdc_Config.IPType < 2)?XRFDC_EN_INVSINC_MASK:XRFDC_MODE_INVSINC_MASK); + (void)XRFdc_GetDecoderMode(InstancePtr, Tile_Id, Block_Id, + &DecoderMode); + BlockStatusPtr->AnalogDataPathStatus |= + (DecoderMode << XRFDC_DIGI_ANALOG_SHIFT4); + + /* FIFO Flags status */ + BlockStatusPtr->IsFIFOFlagsEnabled = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_FABRIC_IMR_OFFSET, XRFDC_FAB_IMR_USRDAT_MASK); + BlockStatusPtr->IsFIFOFlagsAsserted = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_FABRIC_ISR_OFFSET, XRFDC_FAB_ISR_USRDAT_MASK); + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* This API is used to update various QMC settings, eg gain, phase, offset etc. +* QMC settings passed are used to update the corresponding +* block level registers. Driver structure is updated with the new values. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param QMCSettingsPtr is Pointer to the XRFdc_QMC_Settings structure +* in which the QMC settings are passed. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note None +* +******************************************************************************/ +u32 XRFdc_SetQMCSettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_QMC_Settings *QMCSettingsPtr) +{ + u32 Status; + XRFdc_QMC_Settings *QMCConfigPtr; + u32 BaseAddr; + s32 PhaseCorrectionFactor; + u32 GainCorrectionFactor; + u32 Index; + u32 NoOfBlocks; + u32 Offset; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(QMCSettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + Index = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + if (InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Index]. + MixerInputDataType == XRFDC_DATA_TYPE_IQ) { + Index = Block_Id; + NoOfBlocks = XRFDC_NUM_OF_BLKS3; + if (Block_Id == XRFDC_BLK_ID1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks;) { + if (Type == XRFDC_ADC_TILE) { + /* ADC */ + QMCConfigPtr = &InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Analog_Datapath[Index].QMC_Settings; + } else { + QMCConfigPtr = &InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Analog_Datapath[Index].QMC_Settings; + } + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Index); + + if ((QMCSettingsPtr->EnableGain != 0U) && + (QMCSettingsPtr->EnableGain != 1U)) { + metal_log(METAL_LOG_ERROR, "\n Invalid QMC gain option " + "in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((QMCSettingsPtr->EnablePhase != 0U) && + (QMCSettingsPtr->EnablePhase != 1U)) { + metal_log(METAL_LOG_ERROR, "\n Invalid QMC phase option " + "in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((QMCSettingsPtr->PhaseCorrectionFactor <= XRFDC_MIN_PHASE_CORR_FACTOR) || + (QMCSettingsPtr->PhaseCorrectionFactor >= XRFDC_MAX_PHASE_CORR_FACTOR)) { + metal_log(METAL_LOG_ERROR, "\n Invalid QMC Phase Correction " + "factor in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((QMCSettingsPtr->GainCorrectionFactor < XRFDC_MIN_GAIN_CORR_FACTOR) || + (QMCSettingsPtr->GainCorrectionFactor >= XRFDC_MAX_GAIN_CORR_FACTOR)) { + metal_log(METAL_LOG_ERROR, "\n Invalid QMC Gain Correction " + "factor in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + if ((QMCSettingsPtr->EventSource > XRFDC_EVNT_SRC_PL) || + ((QMCSettingsPtr->EventSource == XRFDC_EVNT_SRC_MARKER) && + (Type == XRFDC_ADC_TILE))) { + metal_log(METAL_LOG_ERROR, "\n Invalid event source selection " + "in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE) && + ((QMCSettingsPtr->EventSource == XRFDC_EVNT_SRC_SLICE) || + (QMCSettingsPtr->EventSource == + XRFDC_EVNT_SRC_IMMEDIATE))) { + metal_log(METAL_LOG_ERROR, "\n Invalid Event Source, " + "event source is not supported in 4GSPS ADC %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_QMC_CFG_OFFSET, + XRFDC_QMC_CFG_EN_GAIN_MASK, QMCSettingsPtr->EnableGain); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_QMC_CFG_OFFSET, + XRFDC_QMC_CFG_EN_PHASE_MASK, + (QMCSettingsPtr->EnablePhase << XRFDC_QMC_CFG_PHASE_SHIFT)); + + /* Phase Correction factor is applicable to ADC/DAC IQ mode only */ + if (((Type == XRFDC_ADC_TILE) && + (InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Index]. + MixerInputDataType == XRFDC_DATA_TYPE_IQ)) || + ((Type == XRFDC_DAC_TILE) && + (InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[Index]. + MixerInputDataType == XRFDC_DATA_TYPE_IQ))) { + PhaseCorrectionFactor = + ((QMCSettingsPtr->PhaseCorrectionFactor / XRFDC_MAX_PHASE_CORR_FACTOR) * + XRFDC_QMC_PHASE_MULT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_QMC_PHASE_OFFSET, + XRFDC_QMC_PHASE_CRCTN_MASK, PhaseCorrectionFactor); + } + + /* Gain Correction factor */ + GainCorrectionFactor = ((QMCSettingsPtr->GainCorrectionFactor * + XRFDC_QMC_GAIN_MULT) / XRFDC_MAX_GAIN_CORR_FACTOR); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_QMC_GAIN_OFFSET, + XRFDC_QMC_GAIN_CRCTN_MASK, GainCorrectionFactor); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_QMC_OFF_OFFSET, + XRFDC_QMC_OFFST_CRCTN_MASK, QMCSettingsPtr->OffsetCorrectionFactor); + + /* Event Source */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_QMC_UPDT_OFFSET, + XRFDC_QMC_UPDT_MODE_MASK, QMCSettingsPtr->EventSource); + + if (QMCSettingsPtr->EventSource == XRFDC_EVNT_SRC_IMMEDIATE) { + if (Type == XRFDC_ADC_TILE) { + Offset = XRFDC_ADC_UPDATE_DYN_OFFSET; + } else { + Offset = XRFDC_DAC_UPDATE_DYN_OFFSET; + } + XRFdc_ClrSetReg(InstancePtr, BaseAddr, Offset, + XRFDC_UPDT_EVNT_MASK, XRFDC_UPDT_EVNT_QMC_MASK); + } + /* Update the instance with new values */ + QMCConfigPtr->EventSource = QMCSettingsPtr->EventSource; + QMCConfigPtr->PhaseCorrectionFactor = + QMCSettingsPtr->PhaseCorrectionFactor; + QMCConfigPtr->GainCorrectionFactor = + QMCSettingsPtr->GainCorrectionFactor; + QMCConfigPtr->OffsetCorrectionFactor = + QMCSettingsPtr->OffsetCorrectionFactor; + QMCConfigPtr->EnablePhase = QMCSettingsPtr->EnablePhase; + QMCConfigPtr->EnableGain = QMCSettingsPtr->EnableGain; + if ((Type == XRFDC_ADC_TILE) && + (InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].MixerInputDataType == + XRFDC_DATA_TYPE_IQ) && (XRFdc_IsHighSpeedADC(InstancePtr, + Tile_Id) == 1)) { + Index += XRFDC_BLK_ID2; + } else { + Index += XRFDC_BLK_ID1; + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* QMC settings are returned back to the caller through this API. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param QMCSettingsPtr Pointer to the XRFdc_QMC_Settings structure +* in which the QMC settings are passed. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note None +* +******************************************************************************/ +u32 XRFdc_GetQMCSettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_QMC_Settings *QMCSettingsPtr) +{ + u32 Status; + u32 BaseAddr; + s32 PhaseCorrectionFactor; + u32 GainCorrectionFactor; + s32 OffsetCorrectionFactor; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(QMCSettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1) && (Type == XRFDC_ADC_TILE) && + (InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].MixerInputDataType != + XRFDC_DATA_TYPE_IQ)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Block_Id); + + QMCSettingsPtr->EnableGain = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_QMC_CFG_OFFSET, XRFDC_QMC_CFG_EN_GAIN_MASK); + QMCSettingsPtr->EnablePhase = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_QMC_CFG_OFFSET, XRFDC_QMC_CFG_EN_PHASE_MASK) >> + XRFDC_QMC_CFG_PHASE_SHIFT; + + /* Phase Correction factor */ + if (((Type == XRFDC_ADC_TILE) && + (InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Block_Id]. + MixerInputDataType == XRFDC_DATA_TYPE_IQ)) || + ((Type == XRFDC_DAC_TILE) && + (InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[Block_Id]. + MixerInputDataType == XRFDC_DATA_TYPE_IQ))) { + PhaseCorrectionFactor = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_QMC_PHASE_OFFSET, XRFDC_QMC_PHASE_CRCTN_MASK); + PhaseCorrectionFactor = (PhaseCorrectionFactor >> + 11) == 0 ? PhaseCorrectionFactor : + ((-1 ^ 0xFFF) | PhaseCorrectionFactor); + QMCSettingsPtr->PhaseCorrectionFactor = + ((PhaseCorrectionFactor * XRFDC_MAX_PHASE_CORR_FACTOR) / + XRFDC_QMC_PHASE_MULT); + } else { + QMCSettingsPtr->PhaseCorrectionFactor = 0U; + } + + /* Gain Correction factor */ + GainCorrectionFactor = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_QMC_GAIN_OFFSET, XRFDC_QMC_GAIN_CRCTN_MASK); + QMCSettingsPtr->GainCorrectionFactor = ((GainCorrectionFactor * + XRFDC_MAX_GAIN_CORR_FACTOR) / XRFDC_QMC_GAIN_MULT); + OffsetCorrectionFactor = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_QMC_OFF_OFFSET, XRFDC_QMC_OFFST_CRCTN_MASK); + QMCSettingsPtr->OffsetCorrectionFactor = + (OffsetCorrectionFactor >> 11) == 0 ? OffsetCorrectionFactor : + ((-1 ^ 0xFFF) | OffsetCorrectionFactor); + + /* Event Source */ + QMCSettingsPtr->EventSource = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_QMC_UPDT_OFFSET, XRFDC_QMC_UPDT_MODE_MASK); + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Coarse delay settings passed are used to update the corresponding +* block level registers. Driver structure is updated with the new values. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param CoarseDelaySettingsPtr is Pointer to the XRFdc_CoarseDelay_Settings +* structure in which the CoarseDelay settings are passed. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note None +* +******************************************************************************/ +u32 XRFdc_SetCoarseDelaySettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_CoarseDelay_Settings *CoarseDelaySettingsPtr) +{ + u32 Status; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + u16 Mask; + u16 MaxDelay; + XRFdc_CoarseDelay_Settings *CoarseDelayConfigPtr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(CoarseDelaySettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Mask = (InstancePtr->RFdc_Config.IPType < 2)?XRFDC_CRSE_DLY_CFG_MASK:XRFDC_CRSE_DLY_CFG_MASK_EXT; + MaxDelay = (InstancePtr->RFdc_Config.IPType < 2)?XRFDC_CRSE_DLY_MAX:XRFDC_CRSE_DLY_MAX_EXT; + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + Index = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + if (Type == XRFDC_ADC_TILE) { + CoarseDelayConfigPtr = &InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Analog_Datapath[Index]. + CoarseDelay_Settings; + } else { + CoarseDelayConfigPtr = &InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Analog_Datapath[Index]. + CoarseDelay_Settings; + } + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Index); + + if (CoarseDelaySettingsPtr->CoarseDelay > MaxDelay) { + metal_log(METAL_LOG_ERROR, "\n Requested coarse " + "delay not valid in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((CoarseDelaySettingsPtr->EventSource > XRFDC_EVNT_SRC_PL) || + ((CoarseDelaySettingsPtr->EventSource == XRFDC_EVNT_SRC_MARKER) && + (Type == XRFDC_ADC_TILE))) { + metal_log(METAL_LOG_ERROR, "\n Invalid event " + "source selection in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE) && + ((CoarseDelaySettingsPtr->EventSource == + XRFDC_EVNT_SRC_SLICE) || + (CoarseDelaySettingsPtr->EventSource == + XRFDC_EVNT_SRC_IMMEDIATE))) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid Event " + "Source, event source is not supported in " + "4GSPS ADC %s\r\n", __func__); + goto RETURN_PATH; + } + if (Type == XRFDC_ADC_TILE) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_CRSE_DLY_CFG_OFFSET, Mask, CoarseDelaySettingsPtr->CoarseDelay); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_CRSE_DLY_UPDT_OFFSET, XRFDC_QMC_UPDT_MODE_MASK, + CoarseDelaySettingsPtr->EventSource); + if (CoarseDelaySettingsPtr->EventSource == + XRFDC_EVNT_SRC_IMMEDIATE) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_UPDATE_DYN_OFFSET, XRFDC_UPDT_EVNT_MASK, + XRFDC_ADC_UPDT_CRSE_DLY_MASK); + } + } else { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_DAC_CRSE_DLY_CFG_OFFSET, Mask, + CoarseDelaySettingsPtr->CoarseDelay); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_DAC_CRSE_DLY_UPDT_OFFSET, XRFDC_QMC_UPDT_MODE_MASK, + CoarseDelaySettingsPtr->EventSource); + if (CoarseDelaySettingsPtr->EventSource == + XRFDC_EVNT_SRC_IMMEDIATE) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_DAC_UPDATE_DYN_OFFSET, XRFDC_UPDT_EVNT_MASK, + XRFDC_DAC_UPDT_CRSE_DLY_MASK); + } + } + /* Update the instance with new values */ + CoarseDelayConfigPtr->CoarseDelay = + CoarseDelaySettingsPtr->CoarseDelay; + CoarseDelayConfigPtr->EventSource = + CoarseDelaySettingsPtr->EventSource; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Coarse delay settings are returned back to the caller. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param CoarseDelaySettingsPtr Pointer to the XRFdc_CoarseDelay_Settings +* structure in which the Coarse Delay settings are passed. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note None. +* +******************************************************************************/ +u32 XRFdc_GetCoarseDelaySettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_CoarseDelay_Settings *CoarseDelaySettingsPtr) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(CoarseDelaySettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1) && (Type == XRFDC_ADC_TILE)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Block_Id); + + if (Type == XRFDC_ADC_TILE) { + CoarseDelaySettingsPtr->CoarseDelay = + XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_CRSE_DLY_CFG_OFFSET); + CoarseDelaySettingsPtr->EventSource = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_CRSE_DLY_UPDT_OFFSET, XRFDC_QMC_UPDT_MODE_MASK); + } else { + CoarseDelaySettingsPtr->CoarseDelay = + XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_DAC_CRSE_DLY_CFG_OFFSET); + CoarseDelaySettingsPtr->EventSource = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_CRSE_DLY_UPDT_OFFSET, XRFDC_QMC_UPDT_MODE_MASK); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This function will trigger the update event for an event. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param Event is for which dynamic update event will trigger. +* XRFDC_EVENT_* defines the different events. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Common API for ADC/DAC blocks +* +******************************************************************************/ +u32 XRFdc_UpdateEvent(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, u32 Block_Id, + u32 Event) +{ + u32 Status; + u32 BaseAddr; + u32 EventSource; + u32 NoOfBlocks; + u32 Index; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Index = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + if ((Event == XRFDC_EVENT_QMC) && (InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].MixerInputDataType == + XRFDC_DATA_TYPE_IQ)) { + Index = Block_Id; + NoOfBlocks = XRFDC_NUM_OF_BLKS3; + if (Block_Id == XRFDC_BLK_ID1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + if ((Event != XRFDC_EVENT_MIXER) && (Event != XRFDC_EVENT_QMC) && + (Event != XRFDC_EVENT_CRSE_DLY)) { + metal_log(METAL_LOG_ERROR, "\n Invalid Event " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + for (; Index < NoOfBlocks;) { + /* Identify the Event Source */ + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Index); + if (Event == XRFDC_EVENT_MIXER) { + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, Type, + Tile_Id, Block_Id); + EventSource = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_NCO_UPDT_OFFSET, XRFDC_NCO_UPDT_MODE_MASK); + } else if (Event == XRFDC_EVENT_CRSE_DLY) { + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, + Tile_Id, Block_Id); + EventSource = XRFdc_RDReg(InstancePtr, BaseAddr, + (Type == XRFDC_ADC_TILE) ? XRFDC_ADC_CRSE_DLY_UPDT_OFFSET : + XRFDC_DAC_CRSE_DLY_UPDT_OFFSET, XRFDC_QMC_UPDT_MODE_MASK); + } else { + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, + Tile_Id, Block_Id); + EventSource = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_QMC_UPDT_OFFSET, XRFDC_QMC_UPDT_MODE_MASK); + } + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((EventSource == XRFDC_EVNT_SRC_SYSREF) || + (EventSource == XRFDC_EVNT_SRC_PL) || + (EventSource == XRFDC_EVNT_SRC_MARKER)) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid Event " + "Source, this should be issued external to " + "the driver %s\r\n", __func__); + goto RETURN_PATH; + } + if (Type == XRFDC_ADC_TILE) { + if (EventSource == XRFDC_EVNT_SRC_SLICE) { + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_UPDATE_DYN_OFFSET, 0x1); + } else { + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_HSCOM_ADDR; + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_HSCOM_UPDT_DYN_OFFSET, 0x1); + } + } else { + if (EventSource == XRFDC_EVNT_SRC_SLICE) { + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_DAC_UPDATE_DYN_OFFSET, 0x1); + } else { + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_HSCOM_ADDR; + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_HSCOM_UPDT_DYN_OFFSET, 0x1); + } + } + if ((Event == XRFDC_EVENT_QMC) && (InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].MixerInputDataType == + XRFDC_DATA_TYPE_IQ) && (XRFdc_IsHighSpeedADC(InstancePtr, + Tile_Id) == 1) && (Type == XRFDC_ADC_TILE)) { + Index += XRFDC_BLK_ID2; + } else { + Index += XRFDC_BLK_ID1; + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to set the decimation factor and also update the FIFO write +* words w.r.t to decimation factor. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param DecimationFactor to be set for DAC block. +* XRFDC_INTERP_DECIM_* defines the valid values. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only ADC blocks +* +******************************************************************************/ +u32 XRFdc_SetDecimationFactor(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 DecimationFactor) +{ + u32 Status; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + u16 FabricRate; + u8 DataType; + u32 Factor; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path not " + "enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((DecimationFactor != XRFDC_INTERP_DECIM_OFF) && + (DecimationFactor != XRFDC_INTERP_DECIM_1X) && + (DecimationFactor != XRFDC_INTERP_DECIM_2X) && + (DecimationFactor != XRFDC_INTERP_DECIM_4X) && + (DecimationFactor != XRFDC_INTERP_DECIM_8X) && + ((InstancePtr->RFdc_Config.IPType < 2) || + ((DecimationFactor != XRFDC_INTERP_DECIM_3X) && + (DecimationFactor != XRFDC_INTERP_DECIM_5X) && + (DecimationFactor != XRFDC_INTERP_DECIM_6X) && + (DecimationFactor != XRFDC_INTERP_DECIM_10X) && + (DecimationFactor != XRFDC_INTERP_DECIM_12X) && + (DecimationFactor != XRFDC_INTERP_DECIM_16X) && + (DecimationFactor != XRFDC_INTERP_DECIM_20X) && + (DecimationFactor != XRFDC_INTERP_DECIM_24X) && + (DecimationFactor != XRFDC_INTERP_DECIM_40X)))) { + metal_log(METAL_LOG_ERROR, "\n Invalid Decimation factor " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + DataType = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_DECI_CONFIG_OFFSET, XRFDC_DEC_CFG_MASK); + + /* Decimation factor */ + Factor = DecimationFactor; + if (InstancePtr->RFdc_Config.IPType < 2) { + if (DecimationFactor == XRFDC_INTERP_DECIM_4X) { + Factor = 0x3; + } + if (DecimationFactor == XRFDC_INTERP_DECIM_8X) { + Factor = 0x4; + } + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_DECI_MODE_OFFSET, + XRFDC_DEC_MOD_MASK, Factor); + } else { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_DECI_MODE_OFFSET, + XRFDC_DEC_MOD_MASK_EXT, Factor); + } + + + + /* Fabric rate */ + FabricRate = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_FABRIC_RATE_OFFSET, XRFDC_ADC_FAB_RATE_WR_MASK); + if ((DataType == XRFDC_DECIM_2G_IQ_DATA_TYPE) || + (DataType == XRFDC_DECIM_4G_DATA_TYPE) || + (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1)) { + switch (DecimationFactor) { + case XRFDC_INTERP_DECIM_1X: + FabricRate = XRFDC_FAB_RATE_8; + break; + case XRFDC_INTERP_DECIM_2X: + case XRFDC_INTERP_DECIM_3X: + FabricRate = XRFDC_FAB_RATE_4; + break; + case XRFDC_INTERP_DECIM_4X: + case XRFDC_INTERP_DECIM_5X: + case XRFDC_INTERP_DECIM_6X: + FabricRate = XRFDC_FAB_RATE_2; + break; + case XRFDC_INTERP_DECIM_10X: + case XRFDC_INTERP_DECIM_12X: + case XRFDC_INTERP_DECIM_8X: + FabricRate = XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) ? + XRFDC_FAB_RATE_1:XRFDC_FAB_RATE_2; + break; + case XRFDC_INTERP_DECIM_16X: + case XRFDC_INTERP_DECIM_20X: + case XRFDC_INTERP_DECIM_24X: + case XRFDC_INTERP_DECIM_40X: + FabricRate = XRFDC_FAB_RATE_1; + break; + default: + metal_log(METAL_LOG_DEBUG, "\n Decimation block " + "is OFF in %s\r\n", __func__); + break; + } + } else { + switch (DecimationFactor) { + case XRFDC_INTERP_DECIM_1X: + FabricRate = XRFDC_FAB_RATE_4; + break; + case XRFDC_INTERP_DECIM_2X: + case XRFDC_INTERP_DECIM_3X: + FabricRate = XRFDC_FAB_RATE_2; + break; + case XRFDC_INTERP_DECIM_4X: + case XRFDC_INTERP_DECIM_8X: + case XRFDC_INTERP_DECIM_5X: + case XRFDC_INTERP_DECIM_6X: + case XRFDC_INTERP_DECIM_10X: + case XRFDC_INTERP_DECIM_12X: + case XRFDC_INTERP_DECIM_16X: + case XRFDC_INTERP_DECIM_20X: + case XRFDC_INTERP_DECIM_24X: + case XRFDC_INTERP_DECIM_40X: + FabricRate = XRFDC_FAB_RATE_1; + break; + default: + metal_log(METAL_LOG_DEBUG, "\n Decimation block " + "is OFF in %s\r\n", __func__); + break; + } + } + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_FABRIC_RATE_OFFSET, + XRFDC_ADC_FAB_RATE_WR_MASK, FabricRate); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to set the divider for clock fabric out. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param FabClkDiv to be set for a tile. +* XRFDC_FAB_CLK_* defines the valid divider values. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note ADC and DAC Tiles +* +******************************************************************************/ +u32 XRFdc_SetFabClkOutDiv(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u16 FabClkDiv) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Tile_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested tile not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((FabClkDiv != XRFDC_FAB_CLK_DIV1) && + (FabClkDiv != XRFDC_FAB_CLK_DIV2) && + (FabClkDiv != XRFDC_FAB_CLK_DIV4) && + (FabClkDiv != XRFDC_FAB_CLK_DIV8) && + (FabClkDiv != XRFDC_FAB_CLK_DIV16)) { + metal_log(METAL_LOG_ERROR, "\n Invalid Fabric clock out " + "divider value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + BaseAddr = XRFDC_DRP_BASE(Type, Tile_Id) + XRFDC_HSCOM_ADDR; + + if ((Type == XRFDC_ADC_TILE) && + (FabClkDiv == XRFDC_FAB_CLK_DIV1)) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid clock divider " + "in %s \r\n", __func__); + goto RETURN_PATH; + } else { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_HSCOM_CLK_DIV_OFFSET, + XRFDC_FAB_CLK_DIV_MASK, FabClkDiv); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to get the divider for clock fabric out. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param FabClkDivPtr is a pointer to get fabric clock for a tile. +* XRFDC_FAB_CLK_* defines the valid divider values. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note API is applicable for both ADC and DAC Tiles +* +******************************************************************************/ +u32 XRFdc_GetFabClkOutDiv(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u16 *FabClkDivPtr) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(FabClkDivPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Tile_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested tile not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_DRP_BASE(Type, Tile_Id) + XRFDC_HSCOM_ADDR; + + *FabClkDivPtr = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_HSCOM_CLK_DIV_OFFSET, XRFDC_FAB_CLK_DIV_MASK); + + if ((*FabClkDivPtr < XRFDC_FAB_CLK_DIV1) || + (*FabClkDivPtr > XRFDC_FAB_CLK_DIV16)) { + *FabClkDivPtr = XRFDC_FAB_CLK_DIV16; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to set the interpolation factor and also update the FIFO read +* words w.r.t to interpolation factor. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param InterpolationFactor to be set for DAC block. +* XRFDC_INTERP_DECIM_* defines the valid values. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only DAC blocks +* +******************************************************************************/ +u32 XRFdc_SetInterpolationFactor(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 InterpolationFactor) +{ + u32 Status; + u32 BaseAddr; + u16 FabricRate; + u8 DataType; + u32 Factor; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path not " + "enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((InterpolationFactor != XRFDC_INTERP_DECIM_OFF) && + (InterpolationFactor != XRFDC_INTERP_DECIM_1X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_2X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_4X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_8X) && + ((InstancePtr->RFdc_Config.IPType < 2) || + ((InterpolationFactor != XRFDC_INTERP_DECIM_3X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_5X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_6X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_10X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_12X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_16X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_20X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_24X) && + (InterpolationFactor != XRFDC_INTERP_DECIM_40X)))) { + metal_log(METAL_LOG_ERROR, "\n Invalid Interpolation factor " + "divider value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + + DataType = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_DAC_ITERP_DATA_OFFSET); + if ((DataType == XRFDC_ADC_MIXER_MODE_IQ) && + (InterpolationFactor == + XRFDC_INTERP_DECIM_1X)) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid interpolation " + "factor in %s\r\n", __func__); + goto RETURN_PATH; + } + + /* Interpolation factor */ + Factor = InterpolationFactor; + + if (InstancePtr->RFdc_Config.IPType < 2) { + if (InterpolationFactor == XRFDC_INTERP_DECIM_4X) { + Factor = 0x3; + } + if (InterpolationFactor == XRFDC_INTERP_DECIM_8X) { + Factor = 0x4; + } + } + if (DataType == XRFDC_ADC_MIXER_MODE_IQ) { + Factor |= Factor << ((InstancePtr->RFdc_Config.IPType < 2)?XRFDC_INTERP_MODE_Q_SHIFT:XRFDC_INTERP_MODE_Q_SHIFT_EXT); + } + + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_DAC_INTERP_CTRL_OFFSET, + (InstancePtr->RFdc_Config.IPType < 2)?XRFDC_INTERP_MODE_MASK:XRFDC_INTERP_MODE_MASK_EXT, Factor); + + /* Fabric rate */ + FabricRate = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_FABRIC_RATE_OFFSET, XRFDC_FAB_RATE_RD_MASK); + FabricRate = FabricRate >> XRFDC_FAB_RATE_RD_SHIFT; + if (DataType == XRFDC_ADC_MIXER_MODE_IQ) { + switch (InterpolationFactor) { + case XRFDC_INTERP_DECIM_2X: + case XRFDC_INTERP_DECIM_3X: + FabricRate = XRFDC_FAB_RATE_8; + break; + case XRFDC_INTERP_DECIM_4X: + case XRFDC_INTERP_DECIM_5X: + case XRFDC_INTERP_DECIM_6X: + FabricRate = XRFDC_FAB_RATE_4; + break; + case XRFDC_INTERP_DECIM_8X: + case XRFDC_INTERP_DECIM_10X: + case XRFDC_INTERP_DECIM_12X: + FabricRate = XRFDC_FAB_RATE_2; + break; + case XRFDC_INTERP_DECIM_16X: + case XRFDC_INTERP_DECIM_20X: + case XRFDC_INTERP_DECIM_24X: + case XRFDC_INTERP_DECIM_40X: + FabricRate = XRFDC_FAB_RATE_1; + break; + default: + metal_log(METAL_LOG_DEBUG, "\n Interpolation block " + "is OFF in %s\r\n", __func__); + break; + } + } else { + switch (InterpolationFactor) { + case XRFDC_INTERP_DECIM_1X: + FabricRate = XRFDC_FAB_RATE_8; + break; + case XRFDC_INTERP_DECIM_2X: + case XRFDC_INTERP_DECIM_3X: + FabricRate = XRFDC_FAB_RATE_4; + break; + case XRFDC_INTERP_DECIM_4X: + case XRFDC_INTERP_DECIM_5X: + case XRFDC_INTERP_DECIM_6X: + FabricRate = XRFDC_FAB_RATE_2; + break; + case XRFDC_INTERP_DECIM_8X: + case XRFDC_INTERP_DECIM_10X: + case XRFDC_INTERP_DECIM_12X: + case XRFDC_INTERP_DECIM_16X: + case XRFDC_INTERP_DECIM_20X: + case XRFDC_INTERP_DECIM_24X: + case XRFDC_INTERP_DECIM_40X: + FabricRate = XRFDC_FAB_RATE_1; + break; + default: + metal_log(METAL_LOG_DEBUG, "\n Interpolation block " + "is OFF in %s\r\n", __func__); + break; + } + } + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_FABRIC_RATE_OFFSET, + XRFDC_FAB_RATE_RD_MASK, (FabricRate << XRFDC_FAB_RATE_RD_SHIFT)); + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Interpolation factor are returned back to the caller. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param InterpolationFactorPtr Pointer to return the interpolation factor +* for DAC blocks. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only for DAC blocks +* +******************************************************************************/ +u32 XRFdc_GetInterpolationFactor(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id, u32 *InterpolationFactorPtr) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InterpolationFactorPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path not " + "enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + + if (InstancePtr->RFdc_Config.IPType < 2) { + *InterpolationFactorPtr = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_INTERP_CTRL_OFFSET, XRFDC_INTERP_MODE_I_MASK); + if (*InterpolationFactorPtr == 0x3U) { + *InterpolationFactorPtr = XRFDC_INTERP_DECIM_4X; + } else if (*InterpolationFactorPtr == 0x4U) { + *InterpolationFactorPtr = XRFDC_INTERP_DECIM_8X; + } + } else { + *InterpolationFactorPtr = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_INTERP_CTRL_OFFSET, XRFDC_INTERP_MODE_I_MASK_EXT); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Decimation factor are returned back to the caller. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param DecimationFactorPtr Pointer to return the Decimation factor +* for DAC blocks. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_GetDecimationFactor(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *DecimationFactorPtr) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(DecimationFactorPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path not " + "enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Block_Id); + + + + if (InstancePtr->RFdc_Config.IPType < 2) { + *DecimationFactorPtr = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_DECI_MODE_OFFSET, XRFDC_DEC_MOD_MASK); + if (*DecimationFactorPtr == 0x3U) { + *DecimationFactorPtr = XRFDC_INTERP_DECIM_4X; + } else if (*DecimationFactorPtr == 0x4U) { + *DecimationFactorPtr = XRFDC_INTERP_DECIM_8X; + } + } else { + *DecimationFactorPtr = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_DECI_MODE_OFFSET, XRFDC_DEC_MOD_MASK_EXT); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Fabric data rate for the requested DAC block is set by writing to the +* corresponding register. The function writes the number of valid write words +* for the requested DAC block. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param FabricWrVldWords is write fabric rate to be set for DAC block. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only for DAC blocks +* +******************************************************************************/ +u32 XRFdc_SetFabWrVldWords(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 FabricWrVldWords) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path not " + "enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + + if (FabricWrVldWords > XRFDC_DAC_MAX_WR_FAB_RATE) { + metal_log(METAL_LOG_ERROR, "\n Requested write valid words " + "is Invalid in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_FABRIC_RATE_OFFSET, + XRFDC_DAC_FAB_RATE_WR_MASK, FabricWrVldWords); + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; + +} + +/*****************************************************************************/ +/** +* +* Fabric data rate for the requested ADC block is set by writing to the +* corresponding register. The function writes the number of valid read words +* for the requested ADC block. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC block number inside the tile. Valid values +* are 0-3. +* @param FabricRdVldWords is Read fabric rate to be set for ADC block. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_SetFabRdVldWords(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 FabricRdVldWords) +{ + u32 Status; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path not " + "enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + + if (FabricRdVldWords > XRFDC_ADC_MAX_RD_FAB_RATE) { + metal_log(METAL_LOG_ERROR, "\n Requested read " + "valid words is Invalid in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_FABRIC_RATE_OFFSET, XRFDC_FAB_RATE_RD_MASK, + (FabricRdVldWords << XRFDC_FAB_RATE_RD_SHIFT)); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; + +} + +/*****************************************************************************/ +/** +* +* This API returns the the number of fabric write valid words requested +* for the block. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param FabricWrVldWordsPtr Pointer to return the fabric data rate for +* DAC block +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note ADC/DAC blocks +* +******************************************************************************/ +u32 XRFdc_GetFabWrVldWords(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 *FabricWrVldWordsPtr) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(FabricWrVldWordsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path not " + "enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1) && (Type == XRFDC_ADC_TILE)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Block_Id); + + *FabricWrVldWordsPtr = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_FABRIC_RATE_OFFSET); + if (Type == XRFDC_ADC_TILE) { + *FabricWrVldWordsPtr &= XRFDC_ADC_FAB_RATE_WR_MASK; + } else { + *FabricWrVldWordsPtr &= XRFDC_DAC_FAB_RATE_WR_MASK; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API returns the the number of fabric read valid words requested +* for the block. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param FabricRdVldWordsPtr Pointer to return the fabric data rate for +* ADC/DAC block +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note ADC/DAC blocks +* +******************************************************************************/ +u32 XRFdc_GetFabRdVldWords(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 *FabricRdVldWordsPtr) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(FabricRdVldWordsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path not " + "enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1) && (Type == XRFDC_ADC_TILE)) { + Block_Id = XRFDC_BLK_ID2; + } + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Block_Id); + + *FabricRdVldWordsPtr = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_FABRIC_RATE_OFFSET, XRFDC_FAB_RATE_RD_MASK); + *FabricRdVldWordsPtr = (*FabricRdVldWordsPtr) >> XRFDC_FAB_RATE_RD_SHIFT; + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to clear the Sticky bit in threshold config registers. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param ThresholdToUpdate Select which Threshold (Threshold0 or +* Threshold1 or both) to update. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only ADC blocks +* +******************************************************************************/ +u32 XRFdc_ThresholdStickyClear(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 ThresholdToUpdate) +{ + u32 Status; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((ThresholdToUpdate != XRFDC_UPDATE_THRESHOLD_0) && + (ThresholdToUpdate != XRFDC_UPDATE_THRESHOLD_1) && + (ThresholdToUpdate != XRFDC_UPDATE_THRESHOLD_BOTH)) { + metal_log(METAL_LOG_ERROR, "\n Invalid ThresholdToUpdate " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + if (InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].MixerInputDataType == + XRFDC_DATA_TYPE_IQ) { + Index = Block_Id; + NoOfBlocks = XRFDC_NUM_OF_BLKS3; + if (Block_Id == XRFDC_BLK_ID1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks;) { + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + /* Update for Threshold0 */ + if ((ThresholdToUpdate == XRFDC_UPDATE_THRESHOLD_0) || + (ThresholdToUpdate == XRFDC_UPDATE_THRESHOLD_BOTH)) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD0_CFG_OFFSET, XRFDC_TRSHD0_STIKY_CLR_MASK, + XRFDC_TRSHD0_STIKY_CLR_MASK); + } + /* Update for Threshold1 */ + if ((ThresholdToUpdate == XRFDC_UPDATE_THRESHOLD_1) || + (ThresholdToUpdate == XRFDC_UPDATE_THRESHOLD_BOTH)) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD1_CFG_OFFSET, XRFDC_TRSHD1_STIKY_CLR_MASK, + XRFDC_TRSHD1_STIKY_CLR_MASK); + } + + if ((InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].MixerInputDataType == + XRFDC_DATA_TYPE_IQ) && (XRFdc_IsHighSpeedADC(InstancePtr, + Tile_Id) == 1)) { + Index += XRFDC_BLK_ID2; + } else { + Index += XRFDC_BLK_ID1; + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API sets the threshold clear mode. The clear mode can be through +* explicit DRP access (manual) or auto clear (QMC gain update event). +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADCC block number inside the tile. Valid values +* are 0-3. +* @param ThresholdToUpdate Select which Threshold (Threshold0 or +* Threshold1 or both) to update. +* @param ClrMode can be DRP access (manual) or auto clear (QMC gain +* update event). +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only ADC blocks +* +******************************************************************************/ +u32 XRFdc_SetThresholdClrMode(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id, u32 ThresholdToUpdate, u32 ClrMode) +{ + u32 Status; + u16 ReadReg; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((ThresholdToUpdate != XRFDC_UPDATE_THRESHOLD_0) && + (ThresholdToUpdate != XRFDC_UPDATE_THRESHOLD_1) && + (ThresholdToUpdate != XRFDC_UPDATE_THRESHOLD_BOTH)) { + metal_log(METAL_LOG_ERROR, "\n Invalid ThresholdToUpdate " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + if ((ClrMode != XRFDC_THRESHOLD_CLRMD_MANUAL_CLR) && + (ClrMode != XRFDC_THRESHOLD_CLRMD_AUTO_CLR)) { + metal_log(METAL_LOG_ERROR, "\n Invalid Clear mode " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + if (InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].MixerInputDataType == + XRFDC_DATA_TYPE_IQ) { + Index = Block_Id; + NoOfBlocks = XRFDC_NUM_OF_BLKS3; + if (Block_Id == XRFDC_BLK_ID1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks;) { + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + /* Update for Threshold0 */ + if ((ThresholdToUpdate == XRFDC_UPDATE_THRESHOLD_0) || + (ThresholdToUpdate == XRFDC_UPDATE_THRESHOLD_BOTH)) { + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD0_CFG_OFFSET); + if (ClrMode == XRFDC_THRESHOLD_CLRMD_MANUAL_CLR) { + ReadReg &= ~XRFDC_TRSHD0_CLR_MOD_MASK; + } else { + ReadReg |= XRFDC_TRSHD0_CLR_MOD_MASK; + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD0_CFG_OFFSET, ReadReg); + } + /* Update for Threshold1 */ + if ((ThresholdToUpdate == XRFDC_UPDATE_THRESHOLD_1) || + (ThresholdToUpdate == XRFDC_UPDATE_THRESHOLD_BOTH)) { + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD1_CFG_OFFSET); + if (ClrMode == XRFDC_THRESHOLD_CLRMD_MANUAL_CLR) { + ReadReg &= ~XRFDC_TRSHD1_CLR_MOD_MASK; + } else { + ReadReg |= XRFDC_TRSHD1_CLR_MOD_MASK; + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD1_CFG_OFFSET, ReadReg); + } + if ((InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].MixerInputDataType == + XRFDC_DATA_TYPE_IQ) && (XRFdc_IsHighSpeedADC(InstancePtr, + Tile_Id) == 1)) { + Index += XRFDC_BLK_ID2; + } else { + Index += XRFDC_BLK_ID1; + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Threshold settings are updated into the relevant registers. Driver structure +* is updated with the new values. There can be two threshold settings: +* threshold0 and threshold1. Both of them are independent of each other. +* The function returns the requested threshold (which can be threshold0, +* threshold1, or both. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param ThresholdSettingsPtr Pointer through which the register settings for +* thresholds are passed to the API. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only ADC blocks +* +******************************************************************************/ +u32 XRFdc_SetThresholdSettings(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + XRFdc_Threshold_Settings *ThresholdSettingsPtr) +{ + u32 Status; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + XRFdc_Threshold_Settings *ThresholdConfigPtr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(ThresholdSettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + if (InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].MixerInputDataType == + XRFDC_DATA_TYPE_IQ) { + Index = Block_Id; + NoOfBlocks = XRFDC_NUM_OF_BLKS3; + if (Block_Id == XRFDC_BLK_ID1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks;) { + ThresholdConfigPtr = &InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Analog_Datapath[Index].Threshold_Settings; + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + + if ((ThresholdSettingsPtr->UpdateThreshold != XRFDC_UPDATE_THRESHOLD_0) && + (ThresholdSettingsPtr->UpdateThreshold != XRFDC_UPDATE_THRESHOLD_1) && + (ThresholdSettingsPtr->UpdateThreshold != XRFDC_UPDATE_THRESHOLD_BOTH)) { + metal_log(METAL_LOG_ERROR, "\n Invalid UpdateThreshold " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (((ThresholdSettingsPtr->UpdateThreshold == + XRFDC_UPDATE_THRESHOLD_0) || + (ThresholdSettingsPtr->UpdateThreshold == + XRFDC_UPDATE_THRESHOLD_BOTH)) && + (ThresholdSettingsPtr->ThresholdMode[0] > XRFDC_TRSHD_HYSTERISIS)) { + metal_log(METAL_LOG_ERROR, "\n Requested threshold " + "mode for threshold0 is invalid " + "in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (((ThresholdSettingsPtr->UpdateThreshold == + XRFDC_UPDATE_THRESHOLD_1) || + (ThresholdSettingsPtr->UpdateThreshold == + XRFDC_UPDATE_THRESHOLD_BOTH)) && + (ThresholdSettingsPtr->ThresholdMode[1] > XRFDC_TRSHD_HYSTERISIS)) { + metal_log(METAL_LOG_ERROR, "\n Requested threshold " + "mode for threshold1 is invalid " + "in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + /* Update for Threshold0 */ + if ((ThresholdSettingsPtr->UpdateThreshold == + XRFDC_UPDATE_THRESHOLD_0) || + (ThresholdSettingsPtr->UpdateThreshold == + XRFDC_UPDATE_THRESHOLD_BOTH)) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TRSHD0_CFG_OFFSET, + XRFDC_TRSHD0_EN_MOD_MASK, + ThresholdSettingsPtr->ThresholdMode[0]); + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD0_AVG_LO_OFFSET, + (u16)ThresholdSettingsPtr->ThresholdAvgVal[0]); + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD0_AVG_UP_OFFSET, + (u16)(ThresholdSettingsPtr->ThresholdAvgVal[0] >> + XRFDC_TRSHD0_AVG_UPP_SHIFT)); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD0_UNDER_OFFSET, XRFDC_TRSHD0_UNDER_MASK, + ThresholdSettingsPtr->ThresholdUnderVal[0]); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD0_OVER_OFFSET, XRFDC_TRSHD0_OVER_MASK, + ThresholdSettingsPtr->ThresholdOverVal[0]); + + ThresholdConfigPtr->ThresholdMode[0] = + ThresholdSettingsPtr->ThresholdMode[0]; + ThresholdConfigPtr->ThresholdAvgVal[0] = + ThresholdSettingsPtr->ThresholdAvgVal[0]; + ThresholdConfigPtr->ThresholdUnderVal[0] = + ThresholdSettingsPtr->ThresholdUnderVal[0]; + ThresholdConfigPtr->ThresholdOverVal[0] = + ThresholdSettingsPtr->ThresholdOverVal[0]; + } + + /* Update for Threshold1 */ + if ((ThresholdSettingsPtr->UpdateThreshold == + XRFDC_UPDATE_THRESHOLD_1) || + (ThresholdSettingsPtr->UpdateThreshold == + XRFDC_UPDATE_THRESHOLD_BOTH)) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD1_CFG_OFFSET, XRFDC_TRSHD1_EN_MOD_MASK, + ThresholdSettingsPtr->ThresholdMode[1]); + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD1_AVG_LO_OFFSET, + (u16)ThresholdSettingsPtr->ThresholdAvgVal[1]); + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD1_AVG_UP_OFFSET, + (u16)(ThresholdSettingsPtr->ThresholdAvgVal[1] >> + XRFDC_TRSHD1_AVG_UPP_SHIFT)); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD1_UNDER_OFFSET, XRFDC_TRSHD1_UNDER_MASK, + ThresholdSettingsPtr->ThresholdUnderVal[1]); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD1_OVER_OFFSET, XRFDC_TRSHD1_OVER_MASK, + ThresholdSettingsPtr->ThresholdOverVal[1]); + + ThresholdConfigPtr->ThresholdMode[1] = + ThresholdSettingsPtr->ThresholdMode[1]; + ThresholdConfigPtr->ThresholdAvgVal[1] = + ThresholdSettingsPtr->ThresholdAvgVal[1]; + ThresholdConfigPtr->ThresholdUnderVal[1] = + ThresholdSettingsPtr->ThresholdUnderVal[1]; + ThresholdConfigPtr->ThresholdOverVal[1] = + ThresholdSettingsPtr->ThresholdOverVal[1]; + } + if ((InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].MixerInputDataType == + XRFDC_DATA_TYPE_IQ) && (XRFdc_IsHighSpeedADC(InstancePtr, + Tile_Id) == 1)) { + Index += XRFDC_BLK_ID2; + } else { + Index += XRFDC_BLK_ID1; + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Threshold settings are read from the corresponding registers and are passed +* back to the caller. There can be two threshold settings: +* threshold0 and threshold1. Both of them are independent of each other. +* The function returns the requested threshold (which can be threshold0, +* threshold1, or both. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param ThresholdSettingsPtr Pointer through which the register settings +* for thresholds are passed back.. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_GetThresholdSettings(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + XRFdc_Threshold_Settings *ThresholdSettingsPtr) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(ThresholdSettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1) && (InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].MixerInputDataType != + XRFDC_DATA_TYPE_IQ)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Block_Id); + + /* Threshold mode */ + ThresholdSettingsPtr->UpdateThreshold = XRFDC_UPDATE_THRESHOLD_BOTH; + ThresholdSettingsPtr->ThresholdMode[0] = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD0_CFG_OFFSET, XRFDC_TRSHD0_EN_MOD_MASK); + ThresholdSettingsPtr->ThresholdMode[1] = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_TRSHD1_CFG_OFFSET, XRFDC_TRSHD1_EN_MOD_MASK); + + /* Threshold Average Value */ + ThresholdSettingsPtr->ThresholdAvgVal[0] = XRFdc_ReadReg16(InstancePtr, + BaseAddr, XRFDC_ADC_TRSHD0_AVG_LO_OFFSET); + ThresholdSettingsPtr->ThresholdAvgVal[0] |= (XRFdc_ReadReg16(InstancePtr, + BaseAddr, XRFDC_ADC_TRSHD0_AVG_UP_OFFSET) << + XRFDC_TRSHD0_AVG_UPP_SHIFT); + ThresholdSettingsPtr->ThresholdAvgVal[1] = XRFdc_ReadReg16(InstancePtr, + BaseAddr, XRFDC_ADC_TRSHD1_AVG_LO_OFFSET); + ThresholdSettingsPtr->ThresholdAvgVal[1] |= (XRFdc_ReadReg16(InstancePtr, + BaseAddr, XRFDC_ADC_TRSHD1_AVG_UP_OFFSET) << + XRFDC_TRSHD1_AVG_UPP_SHIFT); + + /* Threshold Under Value */ + ThresholdSettingsPtr->ThresholdUnderVal[0] = XRFdc_RDReg(InstancePtr, + BaseAddr, XRFDC_ADC_TRSHD0_UNDER_OFFSET, XRFDC_TRSHD0_UNDER_MASK); + ThresholdSettingsPtr->ThresholdUnderVal[1] = XRFdc_RDReg(InstancePtr, + BaseAddr, XRFDC_ADC_TRSHD1_UNDER_OFFSET, XRFDC_TRSHD1_UNDER_MASK); + + /* Threshold Over Value */ + ThresholdSettingsPtr->ThresholdOverVal[0] = XRFdc_RDReg(InstancePtr, + BaseAddr, XRFDC_ADC_TRSHD0_OVER_OFFSET, XRFDC_TRSHD0_OVER_MASK); + ThresholdSettingsPtr->ThresholdOverVal[1] = XRFdc_RDReg(InstancePtr, + BaseAddr, XRFDC_ADC_TRSHD1_OVER_OFFSET, XRFDC_TRSHD1_OVER_MASK); + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Decoder mode is updated into the relevant registers. Driver structure is +* updated with the new values. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is DAC block number inside the tile. Valid values +* are 0-3. +* @param DecoderMode Valid values are 1 (Maximum SNR, for non- +* randomized decoder), 2 (Maximum Linearity, for randomized decoder) +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only DAC blocks +* +******************************************************************************/ +u32 XRFdc_SetDecoderMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 DecoderMode) +{ + u32 Status; + u32 *DecoderModeConfigPtr; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + DecoderModeConfigPtr = &InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Analog_Datapath[Block_Id].DecoderMode; + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + + if ((DecoderMode != XRFDC_DECODER_MAX_SNR_MODE) && + (DecoderMode != XRFDC_DECODER_MAX_LINEARITY_MODE)) { + metal_log(METAL_LOG_ERROR, "\n Invalid decoder mode " + "in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_DAC_DECODER_CTRL_OFFSET, + XRFDC_DEC_CTRL_MODE_MASK, DecoderMode); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_DAC_DECODER_CLK_OFFSET, + XRFDC_DEC_CTRL_MODE_MASK, DecoderMode); + *DecoderModeConfigPtr = DecoderMode; + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Decoder mode is read and returned back. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is DAC block number inside the tile. Valid values +* are 0-3. +* @param DecoderModePtr Valid values are 1 (Maximum SNR, for non-randomized +* decoder), 2 (Maximum Linearity, for randomized decoder) +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only for DAC blocks +* +******************************************************************************/ +u32 XRFdc_GetDecoderMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *DecoderModePtr) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(DecoderModePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + *DecoderModePtr = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_DECODER_CTRL_OFFSET, XRFDC_DEC_CTRL_MODE_MASK); + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Resets the NCO phase of the current block phase accumulator. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note None +* +******************************************************************************/ +u32 XRFdc_ResetNCOPhase(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id) +{ + u32 Status; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, Type, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path not " + "enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + + Index = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Index); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_NCO_RST_OFFSET, + XRFDC_NCO_PHASE_RST_MASK, XRFDC_NCO_PHASE_RST_MASK); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; + +} +/*****************************************************************************/ +/** +* +* Enable and Disable the ADC/DAC FIFO. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Enable valid values are 1 (FIFO enable) and 0 (FIFO Disable) +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Common API for ADC/DAC blocks +* +******************************************************************************/ +u32 XRFdc_SetupFIFO(XRFdc *InstancePtr, u32 Type, int Tile_Id, u8 Enable) +{ + u32 Status; + u32 BaseAddr; + u16 NoOfTiles; + u16 Index; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if ((Enable != 0U) && (Enable != 1U)) { + metal_log(METAL_LOG_ERROR, "\n Invalid enable " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + /* An input tile if of -1 selects all tiles */ + if (Tile_Id == XRFDC_SELECT_ALL_TILES) { + NoOfTiles = XRFDC_NUM_OF_TILES4; + Index = XRFDC_TILE_ID0; + } else { + NoOfTiles = Tile_Id + 1; + Index = Tile_Id; + } + + for (; Index < NoOfTiles; Index++) { + BaseAddr = XRFDC_CTRL_STS_BASE(Type, Index); + + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Index); + if ((Status != XRFDC_SUCCESS) && (Tile_Id != XRFDC_SELECT_ALL_TILES)) { + metal_log(METAL_LOG_ERROR, "\n Requested tile%d not " + "available in %s\r\n", Index, __func__); + goto RETURN_PATH; + } else if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_DEBUG, "\n Tile%d not " + "available in %s\r\n", Index, __func__); + continue; + } else { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_FIFO_ENABLE, + XRFDC_FIFO_EN_MASK, (!Enable)); + } + } + Status = XRFDC_SUCCESS; + +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Current status of ADC/DAC FIFO. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param EnablePtr valid values are 1 (FIFO enable) and 0 (FIFO Disable) +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Common API for ADC/DAC blocks +* +******************************************************************************/ +u32 XRFdc_GetFIFOStatus(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 *EnablePtr) +{ + u32 Status; + u32 BaseAddr; + u32 ReadReg; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(EnablePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Tile_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested tile not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_CTRL_STS_BASE(Type, Tile_Id); + + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_FIFO_ENABLE, XRFDC_FIFO_EN_MASK); + *EnablePtr = (!ReadReg); + + Status = XRFDC_SUCCESS; + +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Get Output Current for DAC block. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param OutputCurrPtr pointer to return the output current. +* +* @return +* - Return Output Current for DAC block +* +******************************************************************************/ +u32 XRFdc_GetOutputCurr(XRFdc *InstancePtr, u32 Tile_Id, + u32 Block_Id, u32 *OutputCurrPtr) +{ + u32 Status; + u32 BaseAddr; + u16 ReadReg_Cfg2; + u16 ReadReg_Cfg3; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(OutputCurrPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + + ReadReg_Cfg2 = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_DAC_MC_CFG2_OFFSET, XRFDC_DAC_MC_CFG2_OPCSCAS_MASK); + ReadReg_Cfg3 = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_MC_CFG3_OFFSET, XRFDC_DAC_MC_CFG3_CSGAIN_MASK); + if ((ReadReg_Cfg2 == XRFDC_DAC_MC_CFG2_OPCSCAS_32MA) && + (ReadReg_Cfg3 == XRFDC_DAC_MC_CFG3_CSGAIN_32MA)) { + *OutputCurrPtr = XRFDC_OUTPUT_CURRENT_32MA; + } else if ((ReadReg_Cfg2 == XRFDC_DAC_MC_CFG2_OPCSCAS_20MA) && + (ReadReg_Cfg3 == XRFDC_DAC_MC_CFG3_CSGAIN_20MA)) { + *OutputCurrPtr = XRFDC_OUTPUT_CURRENT_20MA; + } else if ((ReadReg_Cfg2 == 0x0) && (ReadReg_Cfg3 == 0x0)) { + *OutputCurrPtr = 0x0; + } else { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid output " + "current value %s\r\n", __func__); + goto RETURN_PATH; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Set the Nyquist zone. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param NyquistZone valid values are 1 (Odd),2 (Even). +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Common API for ADC/DAC blocks +* +******************************************************************************/ +u32 XRFdc_SetNyquistZone(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 NyquistZone) +{ + u32 Status; + u16 ReadReg; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + u8 CalibrationMode = 0U; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((NyquistZone != XRFDC_ODD_NYQUIST_ZONE) && + (NyquistZone != XRFDC_EVEN_NYQUIST_ZONE)) { + metal_log(METAL_LOG_ERROR, "\n Invalid NyquistZone " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + Index = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Index); + if (Type == XRFDC_ADC_TILE) { + /* Identify calibration mode */ + Status = XRFdc_GetCalibrationMode(InstancePtr, + Tile_Id, Block_Id, &CalibrationMode); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + + if (CalibrationMode == XRFDC_CALIB_MODE1) { + if (NyquistZone == + XRFDC_ODD_NYQUIST_ZONE) { + NyquistZone = + XRFDC_EVEN_NYQUIST_ZONE; + } else { + NyquistZone = + XRFDC_ODD_NYQUIST_ZONE; + } + } + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TI_TISK_CRL0_OFFSET); + if ((NyquistZone % 2U) == 0U) { + ReadReg |= XRFDC_TI_TISK_ZONE_MASK; + } else { + ReadReg &= ~XRFDC_TI_TISK_ZONE_MASK; + } + + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TI_TISK_CRL0_OFFSET, ReadReg); + InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Analog_Datapath[Index]. + NyquistZone = NyquistZone; + } else { + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_DAC_MC_CFG0_OFFSET); + if ((NyquistZone % 2U) == 0U) { + ReadReg |= XRFDC_MC_CFG0_MIX_MODE_MASK; + } else { + ReadReg &= ~XRFDC_MC_CFG0_MIX_MODE_MASK; + } + + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_DAC_MC_CFG0_OFFSET, ReadReg); + InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Analog_Datapath[Index]. + NyquistZone = NyquistZone; + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Get the Nyquist zone. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param NyquistZonePtr Pointer to return the Nyquist zone. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Common API for ADC/DAC blocks +* +******************************************************************************/ +u32 XRFdc_GetNyquistZone(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 *NyquistZonePtr) +{ + u32 Status; + u16 ReadReg; + u32 BaseAddr; + u32 Block; + u8 CalibrationMode = 0U; + u8 MultibandConfig; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(NyquistZonePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (Type == XRFDC_ADC_TILE) { + MultibandConfig = InstancePtr->ADC_Tile[Tile_Id].MultibandConfig; + } else { + MultibandConfig = InstancePtr->DAC_Tile[Tile_Id].MultibandConfig; + } + + if (MultibandConfig != XRFDC_MB_MODE_SB) { + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, Type, + Tile_Id, Block_Id); + } else { + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, + Tile_Id, Block_Id); + } + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + Block = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1) && (Type == XRFDC_ADC_TILE)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Block_Id); + + if (Type == XRFDC_ADC_TILE) { + /* Identify calibration mode */ + Status = XRFdc_GetCalibrationMode(InstancePtr, Tile_Id, + Block, &CalibrationMode); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_TI_TISK_CRL0_OFFSET, XRFDC_TI_TISK_ZONE_MASK); + *NyquistZonePtr = (ReadReg >> XRFDC_TISK_ZONE_SHIFT); + } else { + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_MC_CFG0_OFFSET, XRFDC_MC_CFG0_MIX_MODE_MASK); + *NyquistZonePtr = (ReadReg >> XRFDC_MC_CFG0_MIX_MODE_SHIFT); + } + if (*NyquistZonePtr == 0U) { + *NyquistZonePtr = XRFDC_ODD_NYQUIST_ZONE; + } else { + *NyquistZonePtr = XRFDC_EVEN_NYQUIST_ZONE; + } + + if ((Type == XRFDC_ADC_TILE) && + (CalibrationMode == XRFDC_CALIB_MODE1)) { + if (*NyquistZonePtr == XRFDC_EVEN_NYQUIST_ZONE) { + *NyquistZonePtr = XRFDC_ODD_NYQUIST_ZONE; + } else { + *NyquistZonePtr = XRFDC_EVEN_NYQUIST_ZONE; + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to set the DAC Datapath mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param Mode valid values are 0-3. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled / out of range. +* +* @note Only for DAC blocks +* +******************************************************************************/ +u32 XRFdc_SetDataPathMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 Mode) +{ + u32 Status = XRFDC_SUCCESS; + u32 BaseAddr; + u32 NyquistZone; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + return Status; + } + + if (Mode > XRFDC_DAC_MODE_MAX) { + metal_log(METAL_LOG_ERROR, "\n Invalid Mode " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + return Status; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_DAC_DATAPATH_OFFSET, + XRFDC_DATAPATH_MODE_MASK, Mode); + + NyquistZone = (Mode == XRFDC_DAC_MODE_7G_NQ2) ? + XRFDC_EVEN_NYQUIST_ZONE : XRFDC_ODD_NYQUIST_ZONE; + XRFdc_SetNyquistZone(InstancePtr, XRFDC_DAC_TILE, Tile_Id, Block_Id, NyquistZone); + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to get the DAC Datapath mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param ModePtr pointer used to return value. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Only for DAC blocks +* +******************************************************************************/ +u32 XRFdc_GetDataPathMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *ModePtr) +{ + u32 Status = XRFDC_SUCCESS; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + Xil_AssertNonvoid(ModePtr != NULL); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + return Status; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + *ModePtr = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_DAC_DATAPATH_OFFSET, + XRFDC_DATAPATH_MODE_MASK); + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to set the DAC Image Reject Filter Pass mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param Mode valid values are 0 (for low pass) 1 (for high pass). +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled / bad parameter passed +* +* @note Only for DAC blocks +* +******************************************************************************/ +u32 XRFdc_SetIMRPassMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 Mode) +{ + u32 Status = XRFDC_SUCCESS; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + return Status; + } + + if (Mode > XRFDC_DAC_IMR_MODE_MAX) { + metal_log(METAL_LOG_ERROR, "\n Invalid Mode " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + return Status; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_DAC_DATAPATH_OFFSET, + XRFDC_DATAPATH_IMR_MASK, Mode << 2); + + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to get the DAC Image Reject Filter Pass mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param ModePtr pointer used to return value. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Only for DAC blocks +* +******************************************************************************/ +u32 XRFdc_GetIMRPassMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *ModePtr) +{ + u32 Status = XRFDC_SUCCESS; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + Xil_AssertNonvoid(ModePtr != NULL); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + return Status; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + *ModePtr = (XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_DAC_DATAPATH_OFFSET, + XRFDC_DATAPATH_IMR_MASK)) >> 2; + return Status; +} +/*****************************************************************************/ +/** +* +* This API is to set the Calibration mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param CalibrationMode valid values are 1 and 2. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_SetCalibrationMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u8 CalibrationMode) +{ + u32 Status; + u16 ReadReg; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + XRFdc_Mixer_Settings Mixer_Settings = {0}; + u32 NyquistZone = 0U; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if ((CalibrationMode != XRFDC_CALIB_MODE1) && + (CalibrationMode != XRFDC_CALIB_MODE2)) { + metal_log(METAL_LOG_ERROR, "\n Invalid Calibration mode " + "value in %s\r\n", __func__); + return XRFDC_FAILURE; + } + + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + /* Get Mixer Configurations */ + Status = XRFdc_GetMixerSettings(InstancePtr, XRFDC_ADC_TILE, + Tile_Id, Block_Id, &Mixer_Settings); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + + /* Get Nyquist Zone */ + Status = XRFdc_GetNyquistZone(InstancePtr, XRFDC_ADC_TILE, + Tile_Id, Block_Id, &NyquistZone); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Index); + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TI_DCB_CRL0_OFFSET); + ReadReg &= ~XRFDC_TI_DCB_MODE_MASK; + if (CalibrationMode == XRFDC_CALIB_MODE1) { + if (((Index % 2U) != 0U) && + (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1)) { + ReadReg |= XRFDC_TI_DCB_MODE1_4GSPS; + } else if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) { + ReadReg |= XRFDC_TI_DCB_MODE1_2GSPS; + } + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_TI_DCB_CRL0_OFFSET, ReadReg); + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Analog_Datapath[Index]. + CalibrationMode = CalibrationMode; + } + + /* Set Nyquist Zone */ + Status = XRFdc_SetNyquistZone(InstancePtr, XRFDC_ADC_TILE, + Tile_Id, Block_Id, NyquistZone); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + + /* Set Mixer Configurations */ + Status = XRFdc_SetMixerSettings(InstancePtr, XRFDC_ADC_TILE, + Tile_Id, Block_Id, &Mixer_Settings); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + + Status = XRFDC_SUCCESS; + + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is to get the Calibration mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param CalibrationModePtr pointer to get the calibration mode. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_GetCalibrationMode(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u8 *CalibrationModePtr) +{ + u32 Status; + u16 ReadReg; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(CalibrationModePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (InstancePtr->ADC_Tile[Tile_Id].MultibandConfig != XRFDC_MB_MODE_SB) { + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, XRFDC_ADC_TILE, + Tile_Id, Block_Id); + } else { + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, + Tile_Id, Block_Id); + } + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + if (Block_Id == XRFDC_BLK_ID1) { + Block_Id = XRFDC_BLK_ID3; + } + if (Block_Id == XRFDC_BLK_ID0) { + Block_Id = XRFDC_BLK_ID1; + } + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Block_Id); + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_TI_DCB_CRL0_OFFSET, XRFDC_TI_DCB_MODE_MASK); + if (ReadReg != 0U) { + *CalibrationModePtr = XRFDC_CALIB_MODE1; + } else { + *CalibrationModePtr = XRFDC_CALIB_MODE2; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is used to set the mode for the Inverse-Sinc filter. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is DAC block number inside the tile. Valid values +* are 0-3. +* @param Mode valid values are 0(disable), 1(1st Nyquist zone) + and 2(2nd Nyquist zone). +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled/invalid mode. +* +* @note Only DAC blocks +* +******************************************************************************/ +u32 XRFdc_SetInvSincFIR(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u16 Mode) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (Mode > ((InstancePtr->RFdc_Config.IPType < 2)?XRFDC_INV_SYNC_EN_MAX:XRFDC_INV_SYNC_MODE_MAX)) { + metal_log(METAL_LOG_ERROR, "\n Invalid mode " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_DAC_INVSINC_OFFSET, + (InstancePtr->RFdc_Config.IPType < 2)?XRFDC_EN_INVSINC_MASK:XRFDC_MODE_INVSINC_MASK, Mode); + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is used to get the Inverse-Sinc filter mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is DAC block number inside the tile. Valid values +* are 0-3. +* @param ModePtr is a pointer to get the inv-sinc status. valid values +* are 0(disable), 1(1st Nyquist zone) and 2(2nd Nyquist zone). +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Only DAC blocks +* +******************************************************************************/ +u32 XRFdc_GetInvSincFIR(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u16 *ModePtr) +{ + u32 Status; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + Xil_AssertNonvoid(ModePtr != NULL); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_DAC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, Block_Id); + *ModePtr = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_DAC_INVSINC_OFFSET, (InstancePtr->RFdc_Config.IPType < 2)?XRFDC_EN_INVSINC_MASK:XRFDC_MODE_INVSINC_MASK); + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* Static API to dump ADC registers. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3, and -1. +* +* @return +* None +* +* @note None +* +******************************************************************************/ +static void XRFdc_DumpADCRegs(XRFdc *InstancePtr, int Tile_Id) +{ + u32 BlockId; + u32 Block; + u32 IsBlockAvail; + u32 Offset; + u32 BaseAddr; + u32 ReadReg = 0U; + + for (BlockId = XRFDC_BLK_ID0; BlockId < XRFDC_BLK_ID4; BlockId++) { + Block = BlockId; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + if (BlockId == XRFDC_BLK_ID1) { + Block = XRFDC_BLK_ID0; + } + if ((BlockId == XRFDC_BLK_ID3) || (BlockId == XRFDC_BLK_ID2)) { + Block = XRFDC_BLK_ID1; + } + } + IsBlockAvail = XRFdc_IsADCBlockEnabled(InstancePtr, Tile_Id, + Block); + if (IsBlockAvail == 0U) { + IsBlockAvail = XRFdc_IsADCDigitalPathEnabled(InstancePtr, Tile_Id, + Block); + if (IsBlockAvail == 0U) { + continue; + } + } + metal_log(METAL_LOG_DEBUG, "\n ADC%d%d:: \r\n", Tile_Id, BlockId); + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(BlockId); + for (Offset = 0x0U; Offset <= 0x284U; Offset += 0x4U) { + if ((Offset >= 0x24U && Offset <= 0x2CU) || + (Offset >= 0x48U && Offset <= 0x7CU) || + (Offset >= 0xACU && Offset <= 0xC4U) || + (Offset >= 0x114U && Offset <= 0x13CU) || + (Offset >= 0x188U && Offset <= 0x194U) || + (Offset >= 0x1B8U && Offset <= 0x1BCU) || + (Offset >= 0x1D8U && Offset <= 0x1FCU) || + (Offset >= 0x240U && Offset <= 0x27CU)) { + continue; + } + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, Offset); + metal_log(METAL_LOG_DEBUG, + "\n offset = 0x%x and Value = 0x%x \t", + Offset, ReadReg); + } + } + (void)ReadReg; +} + +/*****************************************************************************/ +/** +* +* Static API to dump DAC registers. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id Valid values are 0-3, and -1. +* +* @return +* None +* +* @note None +* +******************************************************************************/ +static void XRFdc_DumpDACRegs(XRFdc *InstancePtr, int Tile_Id) +{ + u32 BlockId; + u32 IsBlockAvail; + u32 Offset; + u32 BaseAddr; + u32 ReadReg = 0U; + + for (BlockId = XRFDC_BLK_ID0; BlockId < XRFDC_BLK_ID4; BlockId++) { + IsBlockAvail = XRFdc_IsDACBlockEnabled(InstancePtr, Tile_Id, + BlockId); + if (IsBlockAvail == 0U) { + IsBlockAvail = XRFdc_IsDACDigitalPathEnabled(InstancePtr, Tile_Id, + BlockId); + if (IsBlockAvail == 0U) { + continue; + } + } + metal_log(METAL_LOG_DEBUG, "\n DAC%d%d:: \r\n", Tile_Id, BlockId); + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(BlockId); + for (Offset = 0x0U; Offset <= 0x24CU; Offset += 0x4U) { + if ((Offset >= 0x28U && Offset <= 0x34U) || + (Offset >= 0x48U && Offset <= 0x7CU) || + (Offset >= 0xA8U && Offset <= 0xBCU) || + (Offset >= 0xE4U && Offset <= 0xFCU) || + (Offset >= 0x16CU && Offset <= 0x17CU) || + (Offset >= 0x198U && Offset <= 0x1BCU) || + (Offset >= 0x1ECU && Offset <= 0x1FCU) || + (Offset >= 0x204U && Offset <= 0x23CU)) { + continue; + } + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, Offset); + metal_log(METAL_LOG_DEBUG, + "\n offset = 0x%x and Value = 0x%x \t", + Offset, ReadReg); + } + } + (void)ReadReg; +} + +/*****************************************************************************/ +/** +* +* Static API to dump HSCOM registers. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* +* @return +* None +* +* @note None +* +******************************************************************************/ +static void XRFdc_DumpHSCOMRegs(XRFdc *InstancePtr, u32 Type, int Tile_Id) +{ + u32 Offset; + u32 BaseAddr; + u32 ReadReg = 0U; + + if (Type == XRFDC_ADC_TILE) { + metal_log(METAL_LOG_DEBUG, "\n ADC%d HSCOM:: \r\n", Tile_Id); + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + XRFDC_HSCOM_ADDR; + + } else { + metal_log(METAL_LOG_DEBUG, "\n DAC%d HSCOM:: \r\n", Tile_Id); + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id) + XRFDC_HSCOM_ADDR; + } + metal_log(METAL_LOG_DEBUG, "\n Offset\tValue \r\n"); + for (Offset = 0x0U; Offset <= 0x148U; Offset += 0x4U) { + if ((Offset >= 0x60U && Offset <= 0x88U) || + (Offset == 0xBCU) || + (Offset >= 0xC4U && Offset <= 0xFCU) || + (Offset >= 0x110U && Offset <= 0x11CU) || + (Offset >= 0x12CU && Offset <= 0x13CU)) { + continue; + } + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, Offset); + metal_log(METAL_LOG_DEBUG, "\n 0x%x \t 0x%x \t", + Offset, ReadReg); + } + (void)ReadReg; +} + +/*****************************************************************************/ +/** +* +* This Prints the offset of the register along with the content. This API is +* meant to be used for debug purposes. It prints to the console the contents +* of registers for the passed Tile_Id. If -1 is passed, it prints the contents +* of the registers for all the tiles for the respective ADC or DAC +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* +* @return +* None +* +* @note None +* +******************************************************************************/ +void XRFdc_DumpRegs(XRFdc *InstancePtr, u32 Type, int Tile_Id) +{ + u16 NoOfTiles; + u16 Index; + + Xil_AssertVoid(InstancePtr != NULL); + Xil_AssertVoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (Tile_Id == XRFDC_SELECT_ALL_TILES) { + NoOfTiles = XRFDC_NUM_OF_TILES4; + } else { + NoOfTiles = XRFDC_NUM_OF_TILES1; + } + for (Index = XRFDC_TILE_ID0; Index < NoOfTiles; Index++) { + if (NoOfTiles == XRFDC_NUM_OF_TILES4) { + Tile_Id = Index; + } + if (Type == XRFDC_ADC_TILE) { + XRFdc_DumpADCRegs(InstancePtr, Tile_Id); + } else { + XRFdc_DumpDACRegs(InstancePtr, Tile_Id); + } + XRFdc_DumpHSCOMRegs(InstancePtr, Type, Tile_Id); + } +} + +/*****************************************************************************/ +/** +* +* This is a stub for the status callback. The stub is here in case the upper +* layers forget to set the handler. +* +* @param CallBackRefPtr is a pointer to the upper layer callback reference. +* @param Type indicates ADC/DAC. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number (0-3). +* @param StatusEvent indicates one or more interrupt occurred. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +static void StubHandler(void *CallBackRefPtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 StatusEvent) +{ + (void) ((void *)CallBackRefPtr); + (void) Type; + (void) Tile_Id; + (void) Block_Id; + (void) StatusEvent; + + Xil_AssertVoidAlways(); + +} +/*****************************************************************************/ +/** +* +* This function is used to get the Link Coupling mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for 2G, 0-1 for 4G). +* @param ModePtr pointer to get link coupling mode. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_GetLinkCoupling(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *ModePtr) +{ + u32 Status; + u16 ReadReg; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(ModePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Block_Id); + + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_RXPR_MC_CFG0_OFFSET, XRFDC_RX_MC_CFG0_CM_MASK); + if (ReadReg != 0U) { + *ModePtr = XRFDC_LINK_COUPLING_AC; + } else { + *ModePtr = XRFDC_LINK_COUPLING_DC; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This function is used to set the IM3 Dither mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for LS, 0-1 for HS). +* @param Mode 0: Disable +* 1: Enable +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_SetDither(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 Mode) +{ + u32 Status; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if (Mode > XRFDC_DITH_ENABLE) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid Dither Mode " + "in %s\r\n", __func__); + goto RETURN_PATH; + } + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_ADC_DAC_MC_CFG0_OFFSET, XRFDC_RX_MC_CFG0_IM3_DITH_MASK, + (Mode << XRFDC_RX_MC_CFG0_IM3_DITH_SHIFT)); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This function is used to get the IM3 Dither mode. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for LS, 0-1 for HS). +* @param ModePtr pointer to get link coupling mode. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_GetDither(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, + u32 *ModePtr) +{ + u32 Status; + u16 ReadReg; + u32 BaseAddr; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(ModePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Block_Id == XRFDC_BLK_ID1)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Block_Id); + + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_DAC_MC_CFG0_OFFSET, XRFDC_RX_MC_CFG0_IM3_DITH_MASK); + if (ReadReg != 0U) { + *ModePtr = XRFDC_DITH_ENABLE; + } else { + *ModePtr = XRFDC_DITH_DISABLE; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This function is used to set the ADC Signal Detector Settings. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for LS, 0-1 for HS). +* @param SettingsPtr pointer to the XRFdc_Signal_Detector_Settings structure +* to set the signal detector configurations +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled, or invaid values. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_SetSignalDetector(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, XRFdc_Signal_Detector_Settings *SettingsPtr) +{ + u32 Status; + u32 BaseAddr; + u32 Index; + u32 NoOfBlocks; + u16 SignalDetCtrlReg = 0; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(SettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (InstancePtr->RFdc_Config.IPType < 2) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Requested fuctionality not " + "available for this IP in %s\r\n", + __func__); + goto RETURN_PATH; + } + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, Block_Id); + + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Requested block not " + "available in %s\r\n", + __func__); + goto RETURN_PATH; + } + if (SettingsPtr->Mode > XRFDC_SIGDET_MODE_RNDM) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Signal Detector " + "Mode in %s\r\n", + __func__); + goto RETURN_PATH; + } + if (SettingsPtr->EnableIntegrator > XRFDC_ENABLED) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Signal Detector " + "Integrator Enable in %s\r\n", + __func__); + goto RETURN_PATH; + } + if (SettingsPtr->HysteresisEnable > XRFDC_ENABLED) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Signal Detector " + "Hysteresis Enable in %s\r\n", + __func__); + goto RETURN_PATH; + } + if (SettingsPtr->Flush > XRFDC_ENABLED) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Signal Detector " + "Flush Option in %s\r\n", + __func__); + goto RETURN_PATH; + } + if (SettingsPtr->TimeConstant > XRFDC_SIGDET_TC_2_18) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Signal Detector " + "Time Constant in %s\r\n", + __func__); + goto RETURN_PATH; + } + SignalDetCtrlReg |= SettingsPtr->EnableIntegrator << XRFDC_ADC_SIG_DETECT_INTG_SHIFT; + SignalDetCtrlReg |= SettingsPtr->Flush << XRFDC_ADC_SIG_DETECT_FLUSH_SHIFT; + SignalDetCtrlReg |= SettingsPtr->TimeConstant << XRFDC_ADC_SIG_DETECT_TCONST_SHIFT; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + SignalDetCtrlReg |= ((SettingsPtr->Mode << 1) | 1) << XRFDC_ADC_SIG_DETECT_MODE_WRITE_SHIFT; + } else { + SignalDetCtrlReg |= (SettingsPtr->Mode << 1) << XRFDC_ADC_SIG_DETECT_MODE_WRITE_SHIFT; + } + SignalDetCtrlReg |= SettingsPtr->HysteresisEnable << XRFDC_ADC_SIG_DETECT_HYST_SHIFT; + + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_SIG_DETECT_CTRL_OFFSET, XRFDC_ADC_SIG_DETECT_MASK, + SignalDetCtrlReg); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_SIG_DETECT_THRESHOLD0_LEVEL_OFFSET, + XRFDC_ADC_SIG_DETECT_THRESH_MASK, SettingsPtr->HighThreshold); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_SIG_DETECT_THRESHOLD1_LEVEL_OFFSET, + XRFDC_ADC_SIG_DETECT_THRESH_MASK, SettingsPtr->LowThreshold); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This function is used to get the ADC Signal Detector Settings. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for LS, 0-1 for HS). +* @param SettingsPtr pointer to the XRFdc_Signal_Detector_Settings structure +* to get the signal detector configurations +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_GetSignalDetector(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, XRFdc_Signal_Detector_Settings *SettingsPtr) +{ + u32 Status; + u32 BaseAddr; + u16 SignalDetCtrlReg = 0; + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(SettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (InstancePtr->RFdc_Config.IPType < 2) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Requested functionality not " + "available for this IP in %s\r\n", + __func__); + goto RETURN_PATH; + } + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, Block_Id); + + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Requested block not " + "available in %s\r\n", + __func__); + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && (Block_Id == XRFDC_BLK_ID1)) { + Block_Id = XRFDC_BLK_ID2; + } + + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Block_Id); + + SignalDetCtrlReg = + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_ADC_SIG_DETECT_CTRL_OFFSET, XRFDC_ADC_SIG_DETECT_MASK); + SettingsPtr->EnableIntegrator = + (SignalDetCtrlReg & XRFDC_ADC_SIG_DETECT_INTG_MASK) >> XRFDC_ADC_SIG_DETECT_INTG_SHIFT; + SettingsPtr->Flush = (SignalDetCtrlReg & XRFDC_ADC_SIG_DETECT_FLUSH_MASK) >> XRFDC_ADC_SIG_DETECT_FLUSH_SHIFT; + SettingsPtr->TimeConstant = + (SignalDetCtrlReg & XRFDC_ADC_SIG_DETECT_TCONST_MASK) >> XRFDC_ADC_SIG_DETECT_TCONST_SHIFT; + SettingsPtr->Mode = (SignalDetCtrlReg & XRFDC_ADC_SIG_DETECT_MODE_MASK) >> XRFDC_ADC_SIG_DETECT_MODE_READ_SHIFT; + + SettingsPtr->HysteresisEnable = + (SignalDetCtrlReg & XRFDC_ADC_SIG_DETECT_HYST_MASK) >> XRFDC_ADC_SIG_DETECT_HYST_SHIFT; + SettingsPtr->HighThreshold = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_ADC_SIG_DETECT_THRESHOLD0_LEVEL_OFFSET, + XRFDC_ADC_SIG_DETECT_THRESH_MASK); + SettingsPtr->LowThreshold = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_ADC_SIG_DETECT_THRESHOLD1_LEVEL_OFFSET, + XRFDC_ADC_SIG_DETECT_THRESH_MASK); + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This function is used to disable Calibration Coefficients override. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for LS, 0-1 for HS). +* @param CalibrationBlock indicates the calibration block. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if error occurs. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_DisableCoefficientsOverride(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, u32 CalibrationBlock) +{ + u32 BaseAddr; + u32 Status; + u32 Index; + u32 NoOfBlocks; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Requested block not " + "available in %s\r\n", + __func__); + goto RETURN_PATH; + } + + if ((InstancePtr->RFdc_Config.IPType < 2) && (CalibrationBlock == XRFDC_CAL_BLOCK_OCB1)) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Requested functionality not " + "available for this IP in %s\r\n", + __func__); + goto RETURN_PATH; + } + + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + switch (CalibrationBlock) { + case XRFDC_CAL_BLOCK_OCB1: + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL1_OFFSET, XRFDC_CAL_OCB_EN_MASK, + XRFDC_DISABLED); + break; + case XRFDC_CAL_BLOCK_OCB2: + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL3_OFFSET, XRFDC_CAL_OCB_EN_MASK, + XRFDC_DISABLED); + break; + case XRFDC_CAL_BLOCK_GCB: + if (InstancePtr->RFdc_Config.IPType < 2) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL1_OFFSET, + XRFDC_CAL_GCB_ENFL_MASK, XRFDC_CAL_GCB_ACEN_MASK); + /*Clear IP Override Coeffs*/ + XRFdc_ClrSetReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF0_FAB(Index), XRFDC_CAL_GCB_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF1_FAB(Index), XRFDC_CAL_GCB_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF2_FAB(Index), XRFDC_CAL_GCB_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF3_FAB(Index), XRFDC_CAL_GCB_MASK, XRFDC_DISABLED); + } else { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL2_OFFSET, + XRFDC_CAL_GCB_EN_MASK, XRFDC_DISABLED); + } + break; + case XRFDC_CAL_BLOCK_TSCB: + if (InstancePtr->RFdc_Config.IPType < 2) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF0_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF1_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF2_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF3_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF4_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF5_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF6_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF7_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + } else { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF0, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF1, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF2, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF3, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF4, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF5, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF6, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF7, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_DISABLED); + } + break; + default: + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Calibration " + "Mode in %s\r\n", + __func__); + goto RETURN_PATH; + } + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This function is used to set the ADC Calibration Coefficients. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for LS, 0-1 for HS). +* @param CalibrationBlock indicates the block to be written to. +* @param CoeffPtr is pointer to the XRFdc_Calibration_Coefficients structure +* to set the calibration coefficients. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if error occurs. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_SetCalCoefficients(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, u32 CalibrationBlock, + XRFdc_Calibration_Coefficients *CoeffPtr) +{ + u32 BaseAddr; + u32 Status; + u32 Index; + u32 NoOfBlocks; + u32 HighSpeed; + u32 Shift; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(CoeffPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if ((InstancePtr->RFdc_Config.IPType < 2) && (CalibrationBlock == XRFDC_CAL_BLOCK_OCB1)) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Requested functionality not " + "available for this IP in %s\r\n", + __func__); + goto RETURN_PATH; + } + + if (CalibrationBlock == XRFDC_CAL_BLOCK_GCB) { + if ((CoeffPtr->Coeff0 | CoeffPtr->Coeff1 | CoeffPtr->Coeff2 | CoeffPtr->Coeff3) & + ~(XRFDC_CAL_GCB_MASK | (XRFDC_CAL_GCB_MASK << XRFDC_CAL_SLICE_SHIFT))) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Bad Coefficient " + "available for this IP in %s\r\n", + __func__); + goto RETURN_PATH; + } + } + + if (CalibrationBlock == XRFDC_CAL_BLOCK_TSCB) { + if ((CoeffPtr->Coeff0 | CoeffPtr->Coeff1 | CoeffPtr->Coeff2 | CoeffPtr->Coeff3 | CoeffPtr->Coeff4 | + CoeffPtr->Coeff5 | CoeffPtr->Coeff6 | CoeffPtr->Coeff7) & + ~(XRFDC_CAL_TSCB_MASK | (XRFDC_CAL_TSCB_MASK << XRFDC_CAL_SLICE_SHIFT))) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Bad Coefficient " + "available for this IP in %s\r\n", + __func__); + goto RETURN_PATH; + } + } + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Requested block not " + "available in %s\r\n", + __func__); + goto RETURN_PATH; + } + + Index = Block_Id; + HighSpeed = XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id); + if (HighSpeed == XRFDC_ENABLED) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + Shift = HighSpeed ? XRFDC_CAL_SLICE_SHIFT * (Index % 2) : 0; + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + switch (CalibrationBlock) { + case XRFDC_CAL_BLOCK_OCB1: + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL1_OFFSET, XRFDC_CAL_OCB_EN_MASK, + XRFDC_ENABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB1_OFFSET_COEFF0, XRFDC_CAL_OCB_MASK, + CoeffPtr->Coeff0 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB1_OFFSET_COEFF1, XRFDC_CAL_OCB_MASK, + CoeffPtr->Coeff1 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB1_OFFSET_COEFF2, XRFDC_CAL_OCB_MASK, + CoeffPtr->Coeff2 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB1_OFFSET_COEFF3, XRFDC_CAL_OCB_MASK, + CoeffPtr->Coeff3 >> Shift); + break; + case XRFDC_CAL_BLOCK_OCB2: + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL3_OFFSET, XRFDC_CAL_OCB_EN_MASK, + XRFDC_ENABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB2_OFFSET_COEFF0, XRFDC_CAL_OCB_MASK, + CoeffPtr->Coeff0 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB2_OFFSET_COEFF1, XRFDC_CAL_OCB_MASK, + CoeffPtr->Coeff1 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB2_OFFSET_COEFF2, XRFDC_CAL_OCB_MASK, + CoeffPtr->Coeff2 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB2_OFFSET_COEFF3, XRFDC_CAL_OCB_MASK, + CoeffPtr->Coeff3 >> Shift); + break; + case XRFDC_CAL_BLOCK_GCB: + + if (InstancePtr->RFdc_Config.IPType < 2) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL1_OFFSET, + XRFDC_CAL_GCB_ACEN_MASK, XRFDC_DISABLED); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL1_OFFSET, + XRFDC_CAL_GCB_FLSH_MASK, XRFDC_ENABLED << XRFDC_CAL_GCB_FLSH_SHIFT); + XRFdc_ClrSetReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF0_FAB(Index), XRFDC_CAL_GCB_MASK, + CoeffPtr->Coeff0 >> Shift); + XRFdc_ClrSetReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF1_FAB(Index), XRFDC_CAL_GCB_MASK, + CoeffPtr->Coeff1 >> Shift); + XRFdc_ClrSetReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF2_FAB(Index), XRFDC_CAL_GCB_MASK, + CoeffPtr->Coeff2 >> Shift); + XRFdc_ClrSetReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF3_FAB(Index), XRFDC_CAL_GCB_MASK, + CoeffPtr->Coeff3 >> Shift); + } else { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL2_OFFSET, + XRFDC_CAL_GCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_GCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF0, XRFDC_CAL_GCB_MASK, + CoeffPtr->Coeff0 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF1, XRFDC_CAL_GCB_MASK, + CoeffPtr->Coeff1 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF2, XRFDC_CAL_GCB_MASK, + CoeffPtr->Coeff2 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF3, XRFDC_CAL_GCB_MASK, + CoeffPtr->Coeff3 >> Shift); + } + break; + case XRFDC_CAL_BLOCK_TSCB: + if (InstancePtr->RFdc_Config.IPType < 2) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF0_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF1_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF2_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF3_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF4_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF5_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF6_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF7_ALT, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF0_ALT, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff0 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF1_ALT, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff1); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF2_ALT, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff2 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF3_ALT, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff3 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF4_ALT, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff4 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF5_ALT, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff5 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF6_ALT, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff6 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF7_ALT, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff7 >> Shift); + } else { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF0, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF1, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF2, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF3, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF4, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF5, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF6, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF7, + XRFDC_CAL_TSCB_EN_MASK, XRFDC_ENABLED << XRFDC_CAL_TSCB_EN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF0, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff0 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF1, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff1 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF2, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff2 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF3, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff3 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF4, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff4 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF5, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff5 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF6, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff6 >> Shift); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF7, + XRFDC_CAL_TSCB_MASK, CoeffPtr->Coeff7 >> Shift); + } + break; + default: + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Calibration " + "Mode in %s\r\n", + __func__); + goto RETURN_PATH; + } + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This function is used to get the ADC Calibration Coefficients. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for LS, 0-1 for HS). +* @param CalibrationBlock indicates the block to be read from +* @param CoeffPtr is pointer to the XRFdc_Calibration_Coefficients structure +* to get the calibration coefficients. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if error occurs. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_GetCalCoefficients(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, u32 CalibrationBlock, + XRFdc_Calibration_Coefficients *CoeffPtr) +{ + u32 BaseAddr; + u32 Status; + u32 Index; + u32 HighSpeed; + u32 Shift; + u32 NoOfBlocks; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(CoeffPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Requested block not " + "available in %s\r\n", + __func__); + goto RETURN_PATH; + } + + memset(CoeffPtr, 0, sizeof(XRFdc_Calibration_Coefficients)); + Index = Block_Id; + HighSpeed = XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id); + if (HighSpeed == XRFDC_ENABLED) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + for (; Index < NoOfBlocks; Index++) { + BaseAddr = XRFDC_BLOCK_BASE(XRFDC_ADC_TILE, Tile_Id, Index); + Shift = HighSpeed ? XRFDC_CAL_SLICE_SHIFT * (Index % 2) : 0; + switch (CalibrationBlock) { + case XRFDC_CAL_BLOCK_OCB1: + CoeffPtr->Coeff0 |= + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB1_OFFSET_COEFF0, XRFDC_CAL_OCB_MASK) + << Shift; + CoeffPtr->Coeff1 |= + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB1_OFFSET_COEFF1, XRFDC_CAL_OCB_MASK) + << Shift; + CoeffPtr->Coeff2 |= + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB1_OFFSET_COEFF2, XRFDC_CAL_OCB_MASK) + << Shift; + CoeffPtr->Coeff3 |= + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB1_OFFSET_COEFF3, XRFDC_CAL_OCB_MASK) + << Shift; + break; + case XRFDC_CAL_BLOCK_OCB2: + CoeffPtr->Coeff0 |= + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB2_OFFSET_COEFF0, XRFDC_CAL_OCB_MASK) + << Shift; + CoeffPtr->Coeff1 |= + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB2_OFFSET_COEFF1, XRFDC_CAL_OCB_MASK) + << Shift; + CoeffPtr->Coeff2 |= + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB2_OFFSET_COEFF2, XRFDC_CAL_OCB_MASK) + << Shift; + CoeffPtr->Coeff3 |= + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_OCB2_OFFSET_COEFF3, XRFDC_CAL_OCB_MASK) + << Shift; + break; + case XRFDC_CAL_BLOCK_GCB: + if (InstancePtr->RFdc_Config.IPType < 2) { + if (XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_ADC_TI_DCB_CRL1_OFFSET, + XRFDC_CAL_GCB_FLSH_MASK) == XRFDC_DISABLED) { + CoeffPtr->Coeff0 |= + (XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF0_ALT, + XRFDC_CAL_GCB_FAB_MASK) >> + 4) + << Shift; + CoeffPtr->Coeff1 |= + (XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF1_ALT, + XRFDC_CAL_GCB_FAB_MASK) >> + 4) + << Shift; + CoeffPtr->Coeff2 |= + (XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF2_ALT, + XRFDC_CAL_GCB_FAB_MASK) >> + 4) + << Shift; + CoeffPtr->Coeff3 |= + (XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF3_ALT, + XRFDC_CAL_GCB_FAB_MASK) >> + 4) + << Shift; + } else { + CoeffPtr->Coeff0 |= + XRFdc_RDReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF0_FAB(Block_Id), XRFDC_CAL_GCB_MASK) + << Shift; + CoeffPtr->Coeff1 |= + XRFdc_RDReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF1_FAB(Block_Id), XRFDC_CAL_GCB_MASK) + << Shift; + CoeffPtr->Coeff2 |= + XRFdc_RDReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF2_FAB(Block_Id), XRFDC_CAL_GCB_MASK) + << Shift; + CoeffPtr->Coeff3 |= + XRFdc_RDReg(InstancePtr, XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id), + XRFDC_CAL_GCB_COEFF3_FAB(Block_Id), XRFDC_CAL_GCB_MASK) + << Shift; + } + } else { + CoeffPtr->Coeff0 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF0, + XRFDC_CAL_GCB_MASK) + << Shift; + CoeffPtr->Coeff1 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF1, + XRFDC_CAL_GCB_MASK) + << Shift; + CoeffPtr->Coeff2 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF2, + XRFDC_CAL_GCB_MASK) + << Shift; + CoeffPtr->Coeff3 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_GCB_OFFSET_COEFF3, + XRFDC_CAL_GCB_MASK) + << Shift; + } + break; + case XRFDC_CAL_BLOCK_TSCB: + if (InstancePtr->RFdc_Config.IPType < 2) { + CoeffPtr->Coeff0 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF0_ALT, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff1 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF1_ALT, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff2 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF2_ALT, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff3 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF3_ALT, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff4 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF4_ALT, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff5 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF5_ALT, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff6 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF6_ALT, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff7 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF7_ALT, + XRFDC_CAL_TSCB_MASK) + << Shift; + } else { + CoeffPtr->Coeff0 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF0, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff1 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF1, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff2 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF2, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff3 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF3, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff4 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF4, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff5 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF5, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff6 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF6, + XRFDC_CAL_TSCB_MASK) + << Shift; + CoeffPtr->Coeff7 |= XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CAL_TSCB_OFFSET_COEFF7, + XRFDC_CAL_TSCB_MASK) + << Shift; + } + break; + default: + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Calibration " + "Mode in %s\r\n", + __func__); + goto RETURN_PATH; + } + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This function is used to set calibration freeze settings. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for LS, 0-1 for HS). +* @param CalFreezePtr pointer to the settings to be applied. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if error occurs. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_SetCalFreeze(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, XRFdc_Cal_Freeze_Settings *CalFreezePtr) +{ + u32 BaseAddr; + u32 Status; + u32 Index; + u32 NoOfBlocks; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(CalFreezePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (CalFreezePtr->FreezeCalibration > XRFDC_CAL_FREEZE_CALIB) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid FreezeCalibration " + "option in %s\r\n", + __func__); + goto RETURN_PATH; + } + + if (CalFreezePtr->DisableFreezePin > XRFDC_CAL_FRZ_PIN_DISABLE) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid DisableFreezePin " + "option in %s\r\n", + __func__); + goto RETURN_PATH; + } + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Requested block not " + "available in %s\r\n", + __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id); + + Index = Block_Id; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CONV_CAL_STGS(Index), XRFDC_CAL_FREEZE_PIN_MASK, + CalFreezePtr->DisableFreezePin << XRFDC_CAL_FREEZE_PIN_SHIFT); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CONV_CAL_STGS(Index), XRFDC_CAL_FREEZE_CAL_MASK, + CalFreezePtr->FreezeCalibration << XRFDC_CAL_FREEZE_CAL_SHIFT); + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This function is used to get calibration freeze settings and status. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Tile_Id indicates Tile number (0-3). +* @param Block_Id indicates Block number(0-3 for LS, 0-1 for HS). +* @param CalFreezePtr pointer to be filled the settings/status. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if error occurs. +* +* @note Only for ADC blocks +* +******************************************************************************/ +u32 XRFdc_GetCalFreeze(XRFdc *InstancePtr, u32 Tile_Id, u32 Block_Id, XRFdc_Cal_Freeze_Settings *CalFreezePtr) +{ + u32 BaseAddr; + u32 Status; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(CalFreezePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, XRFDC_ADC_TILE, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Requested block not " + "available in %s\r\n", + __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id); + + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + if (Block_Id == XRFDC_BLK_ID1) { + Block_Id = XRFDC_BLK_ID2; + } + } + CalFreezePtr->CalFrozen = + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CONV_CAL_STGS(Block_Id), XRFDC_CAL_FREEZE_STS_MASK) >> + XRFDC_CAL_FREEZE_STS_SHIFT; + CalFreezePtr->DisableFreezePin = + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CONV_CAL_STGS(Block_Id), XRFDC_CAL_FREEZE_PIN_MASK) >> + XRFDC_CAL_FREEZE_PIN_SHIFT; + CalFreezePtr->FreezeCalibration = + XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_CONV_CAL_STGS(Block_Id), XRFDC_CAL_FREEZE_CAL_MASK) >> + XRFDC_CAL_FREEZE_CAL_SHIFT; + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/** @} */ diff --git a/mpm/lib/rfdc/xrfdc_clock.c b/mpm/lib/rfdc/xrfdc_clock.c new file mode 100644 index 000000000..73a19fda5 --- /dev/null +++ b/mpm/lib/rfdc/xrfdc_clock.c @@ -0,0 +1,1801 @@ +/****************************************************************************** +* +* Copyright (C) 2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc_clock.c +* @addtogroup xrfdc_v6_0 +* @{ +* +* Contains the interface functions of the Mixer Settings in XRFdc driver. +* See xrfdc.h for a detailed description of the device and driver. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 6.0 cog 02/17/19 Initial release. +* cog 03/12/19 Invert clock detection bits to support IP change. +* cog 03/12/19 Fix bug where incorrect FS, RefClk and were output +* divider were being returned. +* cog 04/09/19 Discriminate between Gen 3 IP and lower for checking +* if internal PLL is enabled. +* cog 04/09/19 Fixed issue where tile was not autostarting after PLL +* rate change. +* </pre> +* +******************************************************************************/ + +/***************************** Include Files *********************************/ +#include "mpm/rfdc/xrfdc.h" + +/************************** Constant Definitions *****************************/ +static u32 PllTuningMatrix[8][4][2] = { + {{0x7F8A, 0x3FFF}, {0x7F9C, 0x3FFF}, {0x7FE2, 0x3FFF} }, + {{0x7FE9, 0xFFFF}, {0x7F8E, 0xFFFF}, {0x7F9C, 0xFFFF} }, + {{0x7F95, 0xFFFF}, {0x7F8E, 0xFFFF}, { 0x7F9A, 0xFFFF}, {0x7F8C, 0xFFFF} }, + {{0x7F95, 0x3FFF}, {0x7FEE, 0x3FFF}, { 0x7F9A, 0xFFFF}, {0x7F9C, 0xFFFF} }, + {{0x7F95, 0x3FFF}, {0x7FEE, 0x3FFF}, { 0x7F9A, 0xFFFF}, {0x7F9C, 0xFFFF} }, + {{0x7F95, 0xFFFF}, {0x7F8E, 0xFFFF}, { 0x7FEA, 0xFFFF}, {0x7F9C, 0xFFFF} }, + {{0x7FE9, 0xFFFF}, {0x7F8E, 0xFFFF}, { 0x7F9A, 0xFFFF}, {0x7F9C, 0xFFFF} }, + {{0x7FEC, 0xFFFF}, {0x7FEE, 0x3FFF}, { 0x7F9C, 0xFFFF} } +}; + + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static u32 XRFdc_CheckClkDistValid(XRFdc *InstancePtr, XRFdc_Distribution_Settings *DistributionSettingsPtr); +static u32 XRFdc_SetPLLConfig(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + double RefClkFreq, double SamplingRate); + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** +* +* This function is used to set the clock settings +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type indicates ADC/DAC. +* @param Tile_Id indicates Tile number (0-3). +* @param SettingsPtr pointer to set the clock settings +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if no valid distribution found. +* +******************************************************************************/ +u32 XRFdc_SetTileClkSettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, XRFdc_Tile_Clock_Settings *SettingsPtr) +{ + u32 Status; + u32 BaseAddr; + u16 PLLSource; + u16 NetworkCtrlReg; + u16 DistCtrlReg; + u16 PLLRefDivReg; + u16 PLLOpDivReg; + u32 TileIndex; + u16 DivideMode = 0; + u16 DivideValue = 0; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(SettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (InstancePtr->RFdc_Config.IPType < 2) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Requested fuctionality not " + "available for this IP in %s\r\n", + __func__); + goto RETURN_PATH; + } + + TileIndex = (Type == XRFDC_DAC_TILE) ? Tile_Id : Tile_Id + XRFDC_CLK_DST_ADC0; + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Tile_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Requested Tile not " + "available in %s\r\n", + __func__); + goto RETURN_PATH; + } + if (SettingsPtr->SourceTile > XRFDC_CLK_DST_ADC3) { + metal_log(METAL_LOG_ERROR, + "\n Invalid Parameter Value " + "for Source in %s\r\n", + __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (SettingsPtr->DistributedClock > XRFDC_DIST_OUT_OUTDIV) { + metal_log(METAL_LOG_ERROR, + "\n Invalid Parameter Value " + "for Distribution Out in %s\r\n", + __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (SettingsPtr->PLLEnable > 1) { + metal_log(METAL_LOG_ERROR, + "\n Invalid Parameter Value " + "for PLLEnable in %s\r\n", + __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((SettingsPtr->PLLEnable == XRFDC_ENABLED) && (SettingsPtr->DivisionFactor < 1)) { + metal_log(METAL_LOG_ERROR, "\n Invalid Configuration in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((SettingsPtr->DistributedClock == XRFDC_DIST_OUT_OUTDIV) && + ((SettingsPtr->DivisionFactor < 2) && (SettingsPtr->PLLEnable == XRFDC_DISABLED))) { + metal_log(METAL_LOG_ERROR, "\n Invalid Configuration in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((SettingsPtr->SourceTile != TileIndex) && (SettingsPtr->DistributedClock != XRFDC_DIST_OUT_NONE)) { + metal_log(METAL_LOG_ERROR, "\n Cannot Redistribute Clock in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + /*configure PLL & divider or just divider*/ + if (SettingsPtr->PLLEnable == XRFDC_ENABLED) { + metal_log(METAL_LOG_WARNING, + "\n Output divider settings may " + "be overridden in %s\r\n", + __func__); + PLLSource = XRFDC_INTERNAL_PLL_CLK; + Status = XRFdc_DynamicPLLConfig(InstancePtr, Type, Tile_Id, PLLSource, + SettingsPtr->PLLSettings.RefClkFreq, + SettingsPtr->PLLSettings.SampleRate); + SettingsPtr->DivisionFactor = SettingsPtr->PLLSettings.OutputDivider; + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Could not set up PLL " + "in %s\r\n", + __func__); + goto RETURN_PATH; + } + } else if (SettingsPtr->DivisionFactor > 1) { + if (SettingsPtr->DivisionFactor == 2U) { + DivideMode = XRFDC_PLL_OUTDIV_MODE_2; + } else if (SettingsPtr->DivisionFactor == 3U) { + DivideMode = XRFDC_PLL_OUTDIV_MODE_3; + DivideValue = XRFDC_PLL_OUTDIV_MODE_3_VAL; + } else if (SettingsPtr->DivisionFactor >= 4U) { + DivideMode = XRFDC_PLL_OUTDIV_MODE_N; + DivideValue = ((SettingsPtr->DivisionFactor - 4U) >> 1); + } + XRFdc_ClrSetReg(InstancePtr, XRFDC_DRP_BASE(Type, Tile_Id) + XRFDC_HSCOM_ADDR, XRFDC_PLL_DIVIDER0, + XRFDC_PLL_DIVIDER0_MASK, ((DivideMode << XRFDC_PLL_DIVIDER0_SHIFT) | DivideValue)); + } + DistCtrlReg = 0; + PLLRefDivReg = 0; + PLLOpDivReg = 0; + NetworkCtrlReg = 0; + if (SettingsPtr->SourceTile == TileIndex) { + if (SettingsPtr->DistributedClock == XRFDC_DIST_OUT_NONE) { + if (SettingsPtr->PLLEnable == XRFDC_DISABLED) { + PLLRefDivReg |= XRFDC_PLLREFDIV_INPUT_OFF; + NetworkCtrlReg |= XRFDC_NET_CTRL_CLK_REC_DIST_T1; + if (SettingsPtr->DivisionFactor < 2) { + /* + T1 from Self + No PLL + Do Not Use PLL Output Divider + Do Not Distribute + */ + NetworkCtrlReg |= XRFDC_NET_CTRL_CLK_T1_SRC_LOCAL; + DistCtrlReg |= XRFDC_DIST_CTRL_CLK_T1_SRC_LOCAL; + } else { + /* + T1 from Self + No PLL + Do Not Distribute + */ + PLLOpDivReg |= XRFDC_PLLOPDIV_INPUT_DIST_LOCAL; + } + } else { + /* + T1 from Self + PLL + Use PLL Output Divider + Do Not Distribute + */ + NetworkCtrlReg |= XRFDC_NET_CTRL_CLK_REC_PLL; + } + } else { + NetworkCtrlReg |= XRFDC_NET_CTRL_CLK_T1_SRC_DIST; + DistCtrlReg |= XRFDC_DIST_CTRL_TO_T1; + if (SettingsPtr->PLLEnable == XRFDC_DISABLED) { + NetworkCtrlReg |= XRFDC_NET_CTRL_CLK_REC_DIST_T1; + PLLRefDivReg |= XRFDC_PLLREFDIV_INPUT_OFF; + if (SettingsPtr->DivisionFactor < 2) { + /* + T1 From Distribution + No PLL + Do Not Use PLL Output Divider + Send to Distribution + */ + DistCtrlReg |= XRFDC_DIST_CTRL_DIST_SRC_LOCAL; + } else { + /* + T1 From Distribution + No PLL + Use PLL Output Divider + Send to Distribution + */ + PLLOpDivReg |= XRFDC_PLLOPDIV_INPUT_DIST_LOCAL; + DistCtrlReg |= (SettingsPtr->DistributedClock == XRFDC_DIST_OUT_RX) ? + XRFDC_DIST_CTRL_DIST_SRC_LOCAL : + XRFDC_DIST_CTRL_DIST_SRC_PLL; + } + + } else { + /* + T1 From Distribution + PLL + Use PLL Output Divider + Send to Distribution + */ + DistCtrlReg |= (SettingsPtr->DistributedClock == XRFDC_DIST_OUT_RX) ? + XRFDC_DIST_CTRL_DIST_SRC_LOCAL : + XRFDC_DIST_CTRL_DIST_SRC_PLL; + NetworkCtrlReg |= XRFDC_NET_CTRL_CLK_REC_PLL; + } + } + } else { + if (SettingsPtr->PLLEnable == XRFDC_DISABLED) { + PLLRefDivReg |= XRFDC_PLLREFDIV_INPUT_OFF; + if (SettingsPtr->DivisionFactor > 1) { + /* + Source From Distribution + No PLL + Use PLL Output Divider + Do Not Distribute + */ + + PLLOpDivReg |= XRFDC_PLLOPDIV_INPUT_DIST_LOCAL; + NetworkCtrlReg |= XRFDC_NET_CTRL_CLK_INPUT_DIST; + DistCtrlReg |= XRFDC_DIST_CTRL_TO_PLL_DIV; + } else { + /* + Source From Distribution + No PLL + Do Not Use PLL Output Divider + Do Not Distribute + */ + NetworkCtrlReg |= XRFDC_NET_CTRL_CLK_T1_SRC_DIST; + DistCtrlReg |= XRFDC_DIST_CTRL_TO_T1; + } + } else { + /* + Source From Distribution + PLL + Use PLL Output Divider + Do Not Distribute + */ + PLLRefDivReg |= XRFDC_PLLREFDIV_INPUT_DIST; + DistCtrlReg |= XRFDC_DIST_CTRL_TO_PLL_DIV; + } + } + + /*Write to Registers*/ + if (Type == XRFDC_ADC_TILE) { + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id); + } else { + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id); + } + BaseAddr += XRFDC_HSCOM_ADDR; + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK_ALT, DistCtrlReg); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CLK_NETWORK_CTRL1, XRFDC_HSCOM_NETWORK_CTRL1_MASK, NetworkCtrlReg); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_PLL_REFDIV, XRFDC_PLL_REFDIV_MASK, PLLRefDivReg); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_PLL_DIVIDER0, XRFDC_PLL_DIVIDER0_ALT_MASK, PLLOpDivReg); + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This function is used to check the distribution chosen is valid +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param DistributionSettingsPtr pointer to the distribution settings struct +* +* @return +* - XRFDC_SUCCESS if Valid. +* - XRFDC_FAILURE if Not Valid. +* +******************************************************************************/ +static u32 XRFdc_CheckClkDistValid(XRFdc *InstancePtr, XRFdc_Distribution_Settings *DistributionSettingsPtr) +{ + u32 Status; + u8 CurrentTile; + u8 *Source; + u8 Sources[8] = { DistributionSettingsPtr->DAC[0].SourceTile, DistributionSettingsPtr->DAC[1].SourceTile, + DistributionSettingsPtr->DAC[2].SourceTile, DistributionSettingsPtr->DAC[3].SourceTile, + DistributionSettingsPtr->ADC[0].SourceTile, DistributionSettingsPtr->ADC[1].SourceTile, + DistributionSettingsPtr->ADC[2].SourceTile, DistributionSettingsPtr->ADC[3].SourceTile }; + u8 LowBoundary; + u16 EFuse; + XRFdc_Distribution *DistributionPtr; + + /*init for first distribution*/ + DistributionPtr = DistributionSettingsPtr->DistributionStatus; + Source = Sources; + LowBoundary = DistributionSettingsPtr->DAC[0].SourceTile; + DistributionPtr->DistributionSource = DistributionSettingsPtr->DAC[0].SourceTile; + DistributionPtr->Enabled = XRFDC_ENABLED; + DistributionPtr->LowerBound = 0; + + for (CurrentTile = 0; CurrentTile < XRFDC_DIST_MAX; CurrentTile++, Source++) { + if (*Source >= XRFDC_DIST_MAX) { + Status = XRFDC_FAILURE; /*out of range*/ + metal_log(METAL_LOG_ERROR, + "\n Invalid Source " + "value in %s - Out of Range\r\n", + __func__); + goto RETURN_PATH; + } + if (*Source < LowBoundary) { + Status = XRFDC_FAILURE; /*SW: no hopovers*/ + metal_log(METAL_LOG_ERROR, + "\n Invalid Configuration " + "Hopping Over Not Allowed in %s\r\n", + __func__); + goto RETURN_PATH; + } + if (Sources[*Source] != *Source) { /*SW: check source is a distributer*/ + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Source " + "Sourcing from Tile that is not Distributing in %s\r\n", + __func__); + goto RETURN_PATH; + } + if ((*Source == XRFDC_CLK_DST_DAC0) && + (InstancePtr->RFdc_Config.DACTile_Config[XRFDC_CLK_DST_DAC0].NumSlices == 2)) { /*HW: only 2 clks*/ + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Source " + "Sourcing from Tile Without Clock in %s\r\n", + __func__); + goto RETURN_PATH; + } + if ((*Source == XRFDC_CLK_DST_DAC2) && + (InstancePtr->RFdc_Config.DACTile_Config[XRFDC_CLK_DST_DAC2].NumSlices == 2)) { /*HW: only 2 clks*/ + metal_log(METAL_LOG_ERROR, + "\n Invalid Source " + "Sourcing from Tile Without Clock in %s\r\n", + __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((CurrentTile < XRFDC_CLK_DST_ADC0) && + (*Source > XRFDC_CLK_DST_DAC3)) { /*Cut between ADC0 MUX8 && DAC3 STH*/ + metal_log(METAL_LOG_ERROR, + "\n Invalid Configuration " + "DAC Cannot Source from ADC in %s\r\n", + __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (CurrentTile < XRFDC_CLK_DST_ADC0) { /*DAC*/ + EFuse = XRFdc_ReadReg16(InstancePtr, + XRFDC_DRP_BASE(XRFDC_DAC_TILE, CurrentTile) + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_EFUSE_2_OFFSET); + } else { /*ADC*/ + EFuse = XRFdc_ReadReg16(InstancePtr, + XRFDC_DRP_BASE(XRFDC_ADC_TILE, (CurrentTile - XRFDC_CLK_DST_ADC0)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_EFUSE_2_OFFSET); + } + /*if PKG <2*/ + + if ((DistributionSettingsPtr->DAC[0].SourceTile != DistributionSettingsPtr->DAC[1].SourceTile) || + (DistributionSettingsPtr->DAC[0].SourceTile != DistributionSettingsPtr->DAC[2].SourceTile) || + (DistributionSettingsPtr->DAC[0].SourceTile != DistributionSettingsPtr->DAC[3].SourceTile) || + (DistributionSettingsPtr->DAC[0].SourceTile != DistributionSettingsPtr->ADC[0].SourceTile) || + (DistributionSettingsPtr->DAC[0].SourceTile != DistributionSettingsPtr->ADC[1].SourceTile) || + (DistributionSettingsPtr->DAC[0].SourceTile != DistributionSettingsPtr->ADC[2].SourceTile) || + (DistributionSettingsPtr->DAC[0].SourceTile != DistributionSettingsPtr->ADC[3].SourceTile) || + (DistributionSettingsPtr->DAC[0].PLLEnable != XRFDC_ENABLED) || + (DistributionSettingsPtr->DAC[1].PLLEnable != XRFDC_ENABLED) || + (DistributionSettingsPtr->DAC[2].PLLEnable != XRFDC_ENABLED) || + (DistributionSettingsPtr->DAC[3].PLLEnable != XRFDC_ENABLED) || + (DistributionSettingsPtr->ADC[0].PLLEnable != XRFDC_ENABLED) || + (DistributionSettingsPtr->ADC[1].PLLEnable != XRFDC_ENABLED) || + (DistributionSettingsPtr->ADC[2].PLLEnable != XRFDC_ENABLED) || + (DistributionSettingsPtr->ADC[3].PLLEnable != XRFDC_ENABLED)) { /*special case that is allowed.*/ + + /*if PKG <2*/ + if (EFuse & XRFDC_PREMIUMCTRL_CLKDIST) { + if ((CurrentTile > XRFDC_CLK_DST_ADC1) && + (*Source != CurrentTile)) { /*E: no dist past adc1*/ + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Configuration " + "- Licensing - Not Premium in %s\r\n", + __func__); + goto RETURN_PATH; + } + } + /*if PKG <1*/ + if ((EFuse & XRFDC_EXPORTCTRL_CLKDIST) == XRFDC_EXPORTCTRL_CLKDIST) { + if ((CurrentTile > XRFDC_CLK_DST_DAC3) && (*Source != CurrentTile)) { /*E: No ADC Dist*/ + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Configuration " + "- Licensing in %s\r\n", + __func__); + goto RETURN_PATH; + } + if ((CurrentTile == XRFDC_CLK_DST_DAC0) && + (*Source != XRFDC_CLK_DST_DAC1)) { /*E: DAC0 must source from DAC1*/ + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Configuration " + "- Licensing in - Export Control %s\r\n", + __func__); + goto RETURN_PATH; + } + if ((CurrentTile == XRFDC_CLK_DST_DAC2) && + (*Source != XRFDC_CLK_DST_DAC3)) { /*E: DAC2 must source from DAC3*/ + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Configuration " + "- Licensing in %s\r\n", + __func__); + goto RETURN_PATH; + } + } + } + + if (*Source != DistributionPtr->DistributionSource) { /*i.e. if new distribution*/ + DistributionPtr->UpperBound = CurrentTile - 1; + DistributionPtr++; + DistributionPtr->Enabled = XRFDC_ENABLED; + LowBoundary = *Source; + DistributionPtr->DistributionSource = *Source; + DistributionPtr->LowerBound = CurrentTile; + } + } + DistributionPtr->UpperBound = CurrentTile - 1; + Status = XRFDC_SUCCESS; +RETURN_PATH: + if (Status == XRFDC_FAILURE) { + memset(DistributionSettingsPtr, 0, sizeof(XRFdc_Distribution_Settings)); + } + return Status; +} +/*****************************************************************************/ +/** +* +* This function is used to set the clock distribution +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param DistributionSettingsPtr pointer to the distribution settings struct +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if could not set distribution. +* +******************************************************************************/ +u32 XRFdc_SetClkDistribution(XRFdc *InstancePtr, XRFdc_Distribution_Settings *DistributionSettingsPtr) +{ + u32 Status; + u8 DelayLeft; + u8 DelayRight; + s8 Delay; + s8 ClkDetItr; + u8 *Delays[8]; + u8 DelayOutSourceLeft; + u8 DelayOutSourceRight; + XRFdc_Distribution *Distribution; + u8 DistributionCount; + u16 Reg; + u16 ClkDetectReg; + u8 FeedBackForInputRight = 0; + u8 FeedBackForInputLeft = 0; + u8 Tile; + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(DistributionSettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (InstancePtr->RFdc_Config.IPType < 2) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Requested fuctionality not " + "available for this IP in %s\r\n", + __func__); + goto RETURN_PATH; + } + + Status = XRFdc_CheckClkDistValid(InstancePtr, DistributionSettingsPtr); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Invalid Distribution " + "in %s\r\n", + __func__); + goto RETURN_PATH; + } + Delays[0] = &DistributionSettingsPtr->DAC[0].Delay; + Delays[1] = &DistributionSettingsPtr->DAC[1].Delay; + Delays[2] = &DistributionSettingsPtr->DAC[2].Delay; + Delays[3] = &DistributionSettingsPtr->DAC[3].Delay; + Delays[4] = &DistributionSettingsPtr->ADC[0].Delay; + Delays[5] = &DistributionSettingsPtr->ADC[1].Delay; + Delays[6] = &DistributionSettingsPtr->ADC[2].Delay; + Delays[7] = &DistributionSettingsPtr->ADC[3].Delay; + Status = XRFdc_Shutdown(InstancePtr, XRFDC_ADC_TILE, -1); + if (Status != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + Status = XRFdc_Shutdown(InstancePtr, XRFDC_DAC_TILE, -1); + if (Status != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + for (Distribution = DistributionSettingsPtr->DistributionStatus, DistributionCount = 0; + DistributionCount < XRFDC_DIST_MAX; Distribution++, DistributionCount++) { + if (Distribution->Enabled == XRFDC_DISABLED) { + break; + } + DelayLeft = (-Distribution->LowerBound + Distribution->DistributionSource); + DelayRight = (Distribution->UpperBound - Distribution->DistributionSource); + DelayOutSourceLeft = 0; + DelayOutSourceRight = 0; + Distribution->MaxDelay = 0; + Distribution->MinDelay = 255; + Distribution->IsDelayBalanced = 0; + if ((DelayLeft == 0) && (DelayRight == 0)) { /*self contained*/ + Reg = XRFDC_CLK_DISTR_OFF; + } else { + Reg = XRFDC_CLK_DISTR_MUX9_SRC_INT; + if (DelayLeft == 0) { + Reg |= XRFDC_CLK_DISTR_MUX8_SRC_NTH; + } else { + Reg |= XRFDC_CLK_DISTR_MUX8_SRC_INT; + } + if (((Distribution->DistributionSource == XRFDC_CLK_DST_DAC3) || + (Distribution->DistributionSource == XRFDC_CLK_DST_ADC3)) && + ((DelayLeft > 1) || (DelayRight > 1))) /*cases for no FB from tile to right*/ + { + Reg |= XRFDC_CLK_DISTR_MUX4A_SRC_INT | XRFDC_CLK_DISTR_MUX6_SRC_INT | + XRFDC_CLK_DISTR_MUX7_SRC_INT; + FeedBackForInputRight = 0; + FeedBackForInputLeft = 0; + } else { + if (DelayLeft > 1) { + Reg |= XRFDC_CLK_DISTR_MUX4A_SRC_STH | XRFDC_CLK_DISTR_MUX6_SRC_NTH | + XRFDC_CLK_DISTR_MUX7_SRC_INT; + DelayOutSourceRight = 2; + FeedBackForInputRight = 0; + FeedBackForInputLeft = 1; + } else { + Reg |= XRFDC_CLK_DISTR_MUX4A_SRC_INT; + FeedBackForInputRight = 1; + FeedBackForInputLeft = 0; + if ((DelayRight > 1) && + (Distribution->DistributionSource != XRFDC_CLK_DST_DAC2)) { + Reg |= XRFDC_CLK_DISTR_MUX7_SRC_STH; + DelayOutSourceLeft = 2; + } else { + Reg |= XRFDC_CLK_DISTR_MUX7_SRC_INT; + } + if (DelayRight == 0) { + Reg |= XRFDC_CLK_DISTR_MUX6_SRC_OFF; + } else { + Reg |= XRFDC_CLK_DISTR_MUX6_SRC_INT; + } + } + } + } + + *Delays[Distribution->DistributionSource] = + (Reg == XRFDC_CLK_DISTR_OFF) ? 0 : DelayOutSourceLeft + DelayOutSourceRight + 2; + Distribution->MaxDelay = MAX(Distribution->MaxDelay, (*Delays[Distribution->DistributionSource])); + Distribution->MinDelay = MIN(Distribution->MinDelay, (*Delays[Distribution->DistributionSource])); + + /* setup clk detect register */ + ClkDetectReg = (XRFDC_CLOCK_DETECT_CLK << + ((XRFDC_CLK_DST_ADC3 - Distribution->DistributionSource) << 1)); + + if ((Distribution->DistributionSource) < XRFDC_CLK_DST_ADC0) { /*DAC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_DAC_TILE, (Distribution->DistributionSource)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_DAC_TILE, (Distribution->DistributionSource)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } else { /*ADC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_ADC_TILE, + (Distribution->DistributionSource - XRFDC_CLK_DST_ADC0)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_ADC_TILE, + (Distribution->DistributionSource - XRFDC_CLK_DST_ADC0)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } + /*Leftmost tile*/ + if (DelayLeft) { + *Delays[Distribution->LowerBound] = DelayOutSourceLeft + (DelayLeft << 1); + Distribution->MaxDelay = MAX(Distribution->MaxDelay, (*Delays[Distribution->LowerBound])); + Distribution->MinDelay = MIN(Distribution->MinDelay, (*Delays[Distribution->LowerBound])); + Reg = XRFDC_CLK_DISTR_MUX4A_SRC_STH | XRFDC_CLK_DISTR_MUX6_SRC_OFF | + XRFDC_CLK_DISTR_MUX7_SRC_OFF | XRFDC_CLK_DISTR_MUX8_SRC_NTH | + XRFDC_CLK_DISTR_MUX9_SRC_INT; + + /* setup clk detect register */ + ClkDetectReg = (XRFDC_CLOCK_DETECT_BOTH << + ((XRFDC_CLK_DST_ADC3 - Distribution->DistributionSource) << 1)); + for (ClkDetItr = DelayLeft - 1; ClkDetItr > 0; ClkDetItr--) { + ClkDetectReg |= (XRFDC_CLOCK_DETECT_DIST << + ((XRFDC_CLK_DST_ADC3 - (Distribution->DistributionSource - ClkDetItr)) << 1)); + } + + if ((Distribution->DistributionSource - DelayLeft) < XRFDC_CLK_DST_ADC0) { /*DAC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_DAC_TILE, + (Distribution->DistributionSource - DelayLeft)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_DAC_TILE, (Distribution->DistributionSource - DelayLeft)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } else { /*ADC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_ADC_TILE, (Distribution->DistributionSource - + DelayLeft - XRFDC_CLK_DST_ADC0)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_ADC_TILE, + (Distribution->DistributionSource - DelayLeft - XRFDC_CLK_DST_ADC0)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } + } + /*Rest of tiles left of Distribution->DistributionSource*/ + for (Delay = 1; Delay < DelayLeft; Delay++) { + Reg = XRFDC_CLK_DISTR_MUX6_SRC_OFF | XRFDC_CLK_DISTR_MUX7_SRC_STH | + XRFDC_CLK_DISTR_MUX8_SRC_INT | XRFDC_CLK_DISTR_MUX9_SRC_INT; + if (FeedBackForInputLeft == 0) { + Reg |= XRFDC_CLK_DISTR_MUX4A_SRC_STH; + } else { + Reg |= XRFDC_CLK_DISTR_MUX4A_SRC_INT; + } + *Delays[Distribution->DistributionSource - Delay] = + DelayOutSourceLeft + ((Delay + FeedBackForInputLeft) << 1); + Distribution->MaxDelay = + MAX(Distribution->MaxDelay, (*Delays[Distribution->DistributionSource - Delay])); + Distribution->MinDelay = + MIN(Distribution->MinDelay, (*Delays[Distribution->DistributionSource - Delay])); + FeedBackForInputLeft = !FeedBackForInputLeft; + + /* setup clk detect register */ + ClkDetectReg = (XRFDC_CLOCK_DETECT_BOTH << + ((XRFDC_CLK_DST_ADC3 - Distribution->DistributionSource) << 1)); + for (ClkDetItr = Delay - 1; ClkDetItr > 0; ClkDetItr--) { + ClkDetectReg |= (XRFDC_CLOCK_DETECT_DIST << + ((XRFDC_CLK_DST_ADC3 - (Distribution->DistributionSource - ClkDetItr)) << 1)); + } + + if ((Distribution->DistributionSource - Delay) < XRFDC_CLK_DST_ADC0) { /*DAC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_DAC_TILE, + (Distribution->DistributionSource - Delay)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_DAC_TILE, (Distribution->DistributionSource - Delay)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } else { /*ADC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_ADC_TILE, (Distribution->DistributionSource - + Delay - XRFDC_CLK_DST_ADC0)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_ADC_TILE, + (Distribution->DistributionSource - Delay - XRFDC_CLK_DST_ADC0)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } + } + /*Rightmost tile*/ + if (DelayRight) { + Reg = XRFDC_CLK_DISTR_MUX4A_SRC_INT | XRFDC_CLK_DISTR_MUX6_SRC_OFF | + XRFDC_CLK_DISTR_MUX7_SRC_OFF | XRFDC_CLK_DISTR_MUX8_SRC_NTH | + XRFDC_CLK_DISTR_MUX9_SRC_NTH; + *Delays[Distribution->UpperBound] = DelayOutSourceRight + (DelayRight << 1); + Distribution->MaxDelay = MAX(Distribution->MaxDelay, (*Delays[Distribution->UpperBound])); + Distribution->MinDelay = MIN(Distribution->MinDelay, (*Delays[Distribution->UpperBound])); + + /* setup clk detect register */ + ClkDetectReg = (XRFDC_CLOCK_DETECT_BOTH << + ((XRFDC_CLK_DST_ADC3 - Distribution->DistributionSource) << 1)); + for (ClkDetItr = DelayRight - 1; ClkDetItr > 0; ClkDetItr--) { + ClkDetectReg |= (XRFDC_CLOCK_DETECT_DIST << + ((XRFDC_CLK_DST_ADC3 - (Distribution->DistributionSource + ClkDetItr)) << 1)); + } + + if ((Distribution->DistributionSource + DelayRight) < XRFDC_CLK_DST_ADC0) { /*DAC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_DAC_TILE, + (Distribution->DistributionSource + DelayRight)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_DAC_TILE, (Distribution->DistributionSource + DelayRight)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } else { /*ADC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_ADC_TILE, (Distribution->DistributionSource + + DelayRight - XRFDC_CLK_DST_ADC0)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_ADC_TILE, + (Distribution->DistributionSource + DelayRight - XRFDC_CLK_DST_ADC0)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } + } + /*rest of tiles to right*/ + for (Delay = 1; Delay < DelayRight; Delay++) { + if (((Delay + Distribution->DistributionSource) == 3) || (FeedBackForInputRight == 0)) { + FeedBackForInputRight = 0; + Reg = XRFDC_CLK_DISTR_MUX4A_SRC_INT; + *Delays[Distribution->DistributionSource + Delay] = DelayOutSourceRight + (Delay << 1); + } else { + Reg = XRFDC_CLK_DISTR_MUX4A_SRC_STH; + *Delays[Distribution->DistributionSource + Delay] = + DelayOutSourceRight + ((Delay + 1) << 1); + } + Distribution->MaxDelay = + MAX(Distribution->MaxDelay, (*Delays[Distribution->DistributionSource + Delay])); + Distribution->MinDelay = + MIN(Distribution->MinDelay, (*Delays[Distribution->DistributionSource + Delay])); + FeedBackForInputRight = !FeedBackForInputRight; + Reg |= XRFDC_CLK_DISTR_MUX6_SRC_NTH | XRFDC_CLK_DISTR_MUX7_SRC_OFF | + XRFDC_CLK_DISTR_MUX8_SRC_NTH | XRFDC_CLK_DISTR_MUX9_SRC_NTH; + + /* setup clk detect register */ + ClkDetectReg = (XRFDC_CLOCK_DETECT_BOTH << + ((XRFDC_CLK_DST_ADC3 - Distribution->DistributionSource) << 1)); + for (ClkDetItr = Delay - 1; ClkDetItr > 0; ClkDetItr--) { + ClkDetectReg |= (XRFDC_CLOCK_DETECT_DIST << + ((XRFDC_CLK_DST_ADC3 - (Distribution->DistributionSource + ClkDetItr)) << 1)); + } + + if ((Distribution->DistributionSource + Delay) < XRFDC_CLK_DST_ADC0) { /*DAC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_DAC_TILE, + (Distribution->DistributionSource + Delay)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_DAC_TILE, (Distribution->DistributionSource + Delay)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } else { /*ADC*/ + XRFdc_ClrSetReg(InstancePtr, + XRFDC_DRP_BASE(XRFDC_ADC_TILE, (Distribution->DistributionSource + + Delay - XRFDC_CLK_DST_ADC0)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET, XRFDC_HSCOM_CLK_DSTR_MASK, Reg); + XRFdc_ClrSetReg(InstancePtr, + XRFDC_CTRL_STS_BASE(XRFDC_ADC_TILE, + (Distribution->DistributionSource + Delay - XRFDC_CLK_DST_ADC0)), + XRFDC_CLOCK_DETECT_OFFSET, XRFDC_CLOCK_DETECT_MASK, ClkDetectReg); + } + } + Distribution->IsDelayBalanced = (Distribution->MaxDelay == Distribution->MinDelay) ? 1 : 0; + } + for (Tile = 0; Tile < XRFDC_NUM_OF_TILES4; Tile++) { + XRFdc_SetTileClkSettings(InstancePtr, XRFDC_ADC_TILE, Tile, &DistributionSettingsPtr->ADC[Tile]); + XRFdc_SetTileClkSettings(InstancePtr, XRFDC_DAC_TILE, Tile, &DistributionSettingsPtr->DAC[Tile]); + } + Status = XRFdc_StartUp(InstancePtr, XRFDC_ADC_TILE, -1); + if (Status != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + Status = XRFdc_StartUp(InstancePtr, XRFDC_DAC_TILE, -1); + if (Status != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This function is used to get the clock distribution +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param DistributionSettingsPtr pointer to get the distribution settings +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if no valid distribution found. +* +******************************************************************************/ +u32 XRFdc_GetClkDistribution(XRFdc *InstancePtr, XRFdc_Distribution_Settings *DistributionSettingsPtr) +{ + u32 Status; + u16 ReadReg; + u8 *Tile[8]; + u8 CurrentTile; + s8 AdjacentTile; + u8 DelayOutSourceLeft; + u8 DelayOutSourceRight; + u8 *Delays[8]; + XRFdc_Distribution *Distribution; + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(DistributionSettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if (InstancePtr->RFdc_Config.IPType < 2) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Requested fuctionality not " + "available for this IP in %s\r\n", + __func__); + goto RETURN_PATH; + } + + Distribution = DistributionSettingsPtr->DistributionStatus; + Delays[0] = &DistributionSettingsPtr->DAC[0].Delay; + Delays[1] = &DistributionSettingsPtr->DAC[1].Delay; + Delays[2] = &DistributionSettingsPtr->DAC[2].Delay; + Delays[3] = &DistributionSettingsPtr->DAC[3].Delay; + Delays[4] = &DistributionSettingsPtr->ADC[0].Delay; + Delays[5] = &DistributionSettingsPtr->ADC[1].Delay; + Delays[6] = &DistributionSettingsPtr->ADC[2].Delay; + Delays[7] = &DistributionSettingsPtr->ADC[3].Delay; + Tile[0] = &DistributionSettingsPtr->DAC[0].SourceTile; + Tile[1] = &DistributionSettingsPtr->DAC[1].SourceTile; + Tile[2] = &DistributionSettingsPtr->DAC[2].SourceTile; + Tile[3] = &DistributionSettingsPtr->DAC[3].SourceTile; + Tile[4] = &DistributionSettingsPtr->ADC[0].SourceTile; + Tile[5] = &DistributionSettingsPtr->ADC[1].SourceTile; + Tile[6] = &DistributionSettingsPtr->ADC[2].SourceTile; + Tile[7] = &DistributionSettingsPtr->ADC[3].SourceTile; + memset(DistributionSettingsPtr, XRFDC_CLK_DST_INVALID, sizeof(XRFdc_Distribution_Settings)); + + for (CurrentTile = 0; CurrentTile < XRFDC_DIST_MAX; CurrentTile++) { + DelayOutSourceLeft = 0; + DelayOutSourceRight = 0; + if (*Tile[CurrentTile] != XRFDC_CLK_DST_INVALID) { + continue; + } + + if (CurrentTile < XRFDC_CLK_DST_ADC0) { /*DAC*/ + ReadReg = XRFdc_ReadReg16(InstancePtr, + XRFDC_DRP_BASE(XRFDC_DAC_TILE, CurrentTile) + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET) & + XRFDC_HSCOM_CLK_DSTR_MASK; + } else { /*ADC*/ + ReadReg = XRFdc_ReadReg16(InstancePtr, + XRFDC_DRP_BASE(XRFDC_ADC_TILE, (CurrentTile - XRFDC_CLK_DST_ADC0)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET) & + XRFDC_HSCOM_CLK_DSTR_MASK; + } + + if (ReadReg == XRFDC_CLK_DISTR_OFF) { /*it is its own source no dist*/ + *Tile[CurrentTile] = CurrentTile; + *Delays[CurrentTile] = 0; + Distribution->MaxDelay = 0; + Distribution->MinDelay = 0; + Distribution->IsDelayBalanced = 1; + Distribution++; + } else if (ReadReg & (XRFDC_CLK_DISTR_MUX6_SRC_INT | + XRFDC_CLK_DISTR_MUX7_SRC_INT)) { /*it is its own source, distributes its clk*/ + Distribution->MaxDelay = 0; + Distribution->MinDelay = 255; + Distribution->IsDelayBalanced = 0; + *Tile[CurrentTile] = CurrentTile; + if (ReadReg & XRFDC_CLK_DISTR_MUX7_SRC_STH) { + DelayOutSourceLeft = 2; + } else if (ReadReg & XRFDC_CLK_DISTR_MUX6_SRC_NTH) { + DelayOutSourceRight = 2; + } + *Delays[CurrentTile] = DelayOutSourceLeft + DelayOutSourceRight + 2; + Distribution->MaxDelay = MAX(Distribution->MaxDelay, (*Delays[CurrentTile])); + Distribution->MinDelay = MIN(Distribution->MinDelay, (*Delays[CurrentTile])); + /*work right*/ + for (AdjacentTile = CurrentTile + 1; AdjacentTile <= XRFDC_CLK_DST_ADC3; AdjacentTile++) { + if (AdjacentTile < XRFDC_CLK_DST_ADC0) { /*DAC*/ + ReadReg = XRFdc_ReadReg16(InstancePtr, + XRFDC_DRP_BASE(XRFDC_DAC_TILE, AdjacentTile) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET) & + XRFDC_HSCOM_CLK_DSTR_MASK; + } else { /*ADC*/ + ReadReg = XRFdc_ReadReg16(InstancePtr, + XRFDC_DRP_BASE(XRFDC_ADC_TILE, + (AdjacentTile - XRFDC_CLK_DST_ADC0)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET) & + XRFDC_HSCOM_CLK_DSTR_MASK; + } + if ((ReadReg == XRFDC_CLK_DISTR_CONT_RIGHT_EVEN) && + (AdjacentTile != XRFDC_CLK_DST_DAC3)) { + *Tile[AdjacentTile] = CurrentTile; + *Delays[AdjacentTile] = + DelayOutSourceRight + ((AdjacentTile - CurrentTile) << 1) + 2; + Distribution->MaxDelay = MAX(Distribution->MaxDelay, (*Delays[AdjacentTile])); + Distribution->MinDelay = MIN(Distribution->MinDelay, (*Delays[AdjacentTile])); + } else if (ReadReg == XRFDC_CLK_DISTR_CONT_RIGHT_HWL_ODD) { + *Tile[AdjacentTile] = CurrentTile; + *Delays[AdjacentTile] = + DelayOutSourceRight + ((AdjacentTile - CurrentTile) << 1); + Distribution->MaxDelay = MAX(Distribution->MaxDelay, (*Delays[AdjacentTile])); + Distribution->MinDelay = MIN(Distribution->MinDelay, (*Delays[AdjacentTile])); + } else if (ReadReg == XRFDC_CLK_DISTR_RIGHTMOST_TILE) { + *Tile[AdjacentTile] = CurrentTile; + *Delays[AdjacentTile] = + DelayOutSourceRight + ((AdjacentTile - CurrentTile) << 1); + Distribution->MaxDelay = MAX(Distribution->MaxDelay, (*Delays[AdjacentTile])); + Distribution->MinDelay = MIN(Distribution->MinDelay, (*Delays[AdjacentTile])); + break; + } else { + break; + } + } + /*work left*/ + for (AdjacentTile = CurrentTile - 1; AdjacentTile >= XRFDC_CLK_DST_DAC0; AdjacentTile--) { + if (*Tile[AdjacentTile] != XRFDC_CLK_DST_INVALID) { + break; + } + if (AdjacentTile < XRFDC_CLK_DST_ADC0) { /*DAC*/ + ReadReg = XRFdc_ReadReg16(InstancePtr, + XRFDC_DRP_BASE(XRFDC_DAC_TILE, AdjacentTile) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET) & + XRFDC_HSCOM_CLK_DSTR_MASK; + } else { /*ADC*/ + ReadReg = XRFdc_ReadReg16(InstancePtr, + XRFDC_DRP_BASE(XRFDC_ADC_TILE, + (AdjacentTile - XRFDC_CLK_DST_ADC0)) + + XRFDC_HSCOM_ADDR, + XRFDC_HSCOM_CLK_DSTR_OFFSET) & + XRFDC_HSCOM_CLK_DSTR_MASK; + } + if (ReadReg == XRFDC_CLK_DISTR_LEFTMOST_TILE) { + *Tile[AdjacentTile] = CurrentTile; + *Delays[AdjacentTile] = + DelayOutSourceLeft + ((CurrentTile - AdjacentTile) << 1); + break; + } else if (ReadReg == XRFDC_CLK_DISTR_CONT_LEFT_EVEN) { + *Tile[AdjacentTile] = CurrentTile; + *Delays[AdjacentTile] = + DelayOutSourceLeft + ((CurrentTile - AdjacentTile) << 1) + 2; + } else if (ReadReg == XRFDC_CLK_DISTR_CONT_LEFT_ODD) { + *Delays[AdjacentTile] = + DelayOutSourceLeft + ((CurrentTile - AdjacentTile) << 1); + *Tile[AdjacentTile] = CurrentTile; + } else { + break; + } + Distribution->MaxDelay = MAX(Distribution->MaxDelay, (*Delays[AdjacentTile])); + Distribution->MinDelay = MIN(Distribution->MinDelay, (*Delays[AdjacentTile])); + } + Distribution->IsDelayBalanced = (Distribution->MaxDelay == Distribution->MinDelay) ? 1 : 0; + Distribution++; + } + } + Status = XRFdc_CheckClkDistValid(InstancePtr, DistributionSettingsPtr); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Invalid Distribution " + "in %s\r\n", + __func__); + goto RETURN_PATH; + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* This function gets Clock source +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type indicates ADC/DAC. +* @param Tile_Id indicates Tile number (0-3). +* @param ClockSourcePtr Pointer to return the clock source +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note None. +* +******************************************************************************/ +u32 XRFdc_GetClockSource(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 *ClockSourcePtr) +{ + u32 BaseAddr; + u32 Status; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(ClockSourcePtr != NULL); + + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Tile_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested tile not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_DRP_BASE(Type, Tile_Id) + XRFDC_HSCOM_ADDR; + + *ClockSourcePtr = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_CLK_NETWORK_CTRL1, XRFDC_CLK_NETWORK_CTRL1_USE_PLL_MASK); + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This function gets PLL lock status +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type indicates ADC/DAC. +* @param Tile_Id indicates Tile number (0-3). +* @param LockStatusPtr Pointer to return the PLL lock status +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note None. +* +******************************************************************************/ +u32 XRFdc_GetPLLLockStatus(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 *LockStatusPtr) +{ + u32 BaseAddr; + u32 ReadReg; + u32 ClkSrc = 0U; + u32 Status; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(LockStatusPtr != NULL); + + /* + * Get Tile clock source information + */ + if (XRFdc_GetClockSource(InstancePtr, Type, Tile_Id, &ClkSrc) + != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Get clock source request Tile %d " + "failed in %s\r\n", Tile_Id, __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + if (ClkSrc == XRFDC_EXTERNAL_CLK) { + metal_log(METAL_LOG_DEBUG, "\n Requested Tile %d " + "uses external clock source in %s\r\n", Tile_Id, __func__); + *LockStatusPtr = XRFDC_PLL_LOCKED; + } else { + if (Type == XRFDC_ADC_TILE) { + BaseAddr = XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id); + } else { + BaseAddr = XRFDC_DAC_TILE_CTRL_STATS_ADDR(Tile_Id); + } + + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_STATUS_OFFSET, + XRFDC_PLL_LOCKED_MASK); + if (ReadReg != 0U) { + *LockStatusPtr = XRFDC_PLL_LOCKED; + } else { + *LockStatusPtr = XRFDC_PLL_UNLOCKED; + } + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* +* This function used for configuring the internal PLL registers +* based on reference clock and sampling rate +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type indicates ADC/DAC. +* @param Tile_Id indicates Tile number (0-3). +* @param RefClkFreq Reference Clock Frequency MHz(50MHz - 1.2GHz) +* @param SamplingRate Sampling Rate in MHz(0.5- 4 GHz) +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note None. +* +******************************************************************************/ +static u32 XRFdc_SetPLLConfig(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + double RefClkFreq, double SamplingRate) +{ + u32 BaseAddr; + u32 Status; + u32 FeedbackDiv; + u32 OutputDiv; + double CalcSamplingRate; + double PllFreq; + double SamplingError; + u32 Best_FeedbackDiv = 0x0U; + u32 Best_OutputDiv = 0x2U; + double Best_Error = 0xFFFFFFFFU; + u32 DivideMode = 0x0U; + u32 DivideValue = 0x0U; + u32 PllFreqIndex = 0x0U; + u32 FbDivIndex = 0x0U; + u32 RefClkDiv = 0x1; + u16 ReadReg; + + if (Type == XRFDC_ADC_TILE) { + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id); + } else { + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id); + } + + BaseAddr += XRFDC_HSCOM_ADDR; + + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, XRFDC_PLL_REFDIV); + if (ReadReg & XRFDC_REFCLK_DIV_1_MASK) { + RefClkDiv = XRFDC_REF_CLK_DIV_1; + } else { + switch (ReadReg & XRFDC_REFCLK_DIV_MASK) { + case XRFDC_REFCLK_DIV_2_MASK: + RefClkDiv = XRFDC_REF_CLK_DIV_2; + break; + case XRFDC_REFCLK_DIV_3_MASK: + RefClkDiv = XRFDC_REF_CLK_DIV_3; + break; + case XRFDC_REFCLK_DIV_4_MASK: + RefClkDiv = XRFDC_REF_CLK_DIV_4; + break; + default: + /* + * IP currently supporting 1 to 4 divider values. This + * error condition might change in future based on IP update. + */ + metal_log(METAL_LOG_ERROR, "\n Unsupported Reference " + "clock Divider value in %s\r\n", __func__); + return XRFDC_FAILURE; + } + } + + RefClkFreq /= RefClkDiv; + + /* + * Sweep valid integer values of FeedbackDiv(N) and record a list + * of values that fall in the valid VCO range 8.5GHz - 12.8GHz + */ + for (FeedbackDiv = PLL_FPDIV_MIN; FeedbackDiv <= PLL_FPDIV_MAX; + FeedbackDiv++) { + + PllFreq = FeedbackDiv * RefClkFreq; + + if ((PllFreq >= VCO_RANGE_MIN) && (PllFreq <= VCO_RANGE_MAX)) { + /* + * Sweep values of OutputDiv(M) to find the output frequency + * that best matches the user requested value + */ + + for (OutputDiv = PLL_DIVIDER_MIN; OutputDiv <= PLL_DIVIDER_MAX; + OutputDiv += 2U) { + + CalcSamplingRate = (PllFreq / OutputDiv); + + if (SamplingRate > CalcSamplingRate) { + SamplingError = SamplingRate - CalcSamplingRate; + } else { + SamplingError = CalcSamplingRate - SamplingRate; + } + + if (Best_Error > SamplingError) { + Best_FeedbackDiv = FeedbackDiv; + Best_OutputDiv = OutputDiv; + Best_Error = SamplingError; + } + } + + OutputDiv = 3U; + CalcSamplingRate = (PllFreq / OutputDiv); + + if (SamplingRate > CalcSamplingRate) { + SamplingError = SamplingRate - CalcSamplingRate; + } else { + SamplingError = CalcSamplingRate - SamplingRate; + } + + if (Best_Error > SamplingError) { + Best_FeedbackDiv = FeedbackDiv; + Best_OutputDiv = OutputDiv; + Best_Error = SamplingError; + } + } + + /* + * PLL Static configuration + */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_SDM_CFG0, 0x80U); + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_SDM_SEED0, 0x111U); + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_SDM_SEED1, 0x11U); + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_VREG, 0x45U); + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_VCO0, 0x5800U); + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_VCO1, 0x08U); + + /* + * Set Feedback divisor value + */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_FPDIV, + Best_FeedbackDiv - 2U); + + /* + * Set Output divisor value + */ + if (Best_OutputDiv == 2U) { + DivideMode = 0x1U; + } else if (Best_OutputDiv == 3U) { + DivideMode = 0x2U; + DivideValue = 0x1U; + } else if (Best_OutputDiv >= 4U) { + DivideMode = 0x3U; + DivideValue = ((Best_OutputDiv - 4U)/2U); + } + + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_PLL_DIVIDER0, + XRFDC_PLL_DIVIDER0_MASK, ((DivideMode << XRFDC_PLL_DIVIDER0_SHIFT) | DivideValue)); + + /* + * Enable fine sweep + */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_CRS2, XRFDC_PLL_CRS2_VAL); + + /* + * Set default PLL spare inputs LSB + */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_SPARE0, 0x507U); + + /* + * Set PLL spare inputs MSB + */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_SPARE1, 0x0U); + + PllFreq = RefClkFreq * Best_FeedbackDiv; + + if (PllFreq < 9400U) { + PllFreqIndex = 0U; + FbDivIndex = 2U; + if (Best_FeedbackDiv < 21U) { + FbDivIndex = 0U; + } else if (Best_FeedbackDiv < 30U) { + FbDivIndex = 1U; + } + } else if (PllFreq < 10070U) { + PllFreqIndex = 1U; + FbDivIndex = 2U; + if (Best_FeedbackDiv < 18U) { + FbDivIndex = 0U; + } else if (Best_FeedbackDiv < 30U) { + FbDivIndex = 1U; + } + } else if (PllFreq < 10690U) { + PllFreqIndex = 2U; + FbDivIndex = 3U; + if (Best_FeedbackDiv < 18U) { + FbDivIndex = 0U; + } else if (Best_FeedbackDiv < 25U) { + FbDivIndex = 1U; + } else if (Best_FeedbackDiv < 35U) { + FbDivIndex = 2U; + } + } else if (PllFreq < 10990U) { + PllFreqIndex = 3U; + FbDivIndex = 3U; + if (Best_FeedbackDiv < 19U) { + FbDivIndex = 0U; + } else if (Best_FeedbackDiv < 27U) { + FbDivIndex = 1U; + } else if (Best_FeedbackDiv < 38U) { + FbDivIndex = 2U; + } + } else if (PllFreq < 11430U) { + PllFreqIndex = 4U; + FbDivIndex = 3U; + if (Best_FeedbackDiv < 19U) { + FbDivIndex = 0U; + } else if (Best_FeedbackDiv < 27U) { + FbDivIndex = 1U; + } else if (Best_FeedbackDiv < 38U) { + FbDivIndex = 2U; + } + } else if (PllFreq < 12040U) { + PllFreqIndex = 5U; + FbDivIndex = 3U; + if (Best_FeedbackDiv < 20U) { + FbDivIndex = 0U; + } else if (Best_FeedbackDiv < 28U) { + FbDivIndex = 1U; + } else if (Best_FeedbackDiv < 40U) { + FbDivIndex = 2U; + } + } else if (PllFreq < 12530U) { + PllFreqIndex = 6U; + FbDivIndex = 3U; + if (Best_FeedbackDiv < 23U) { + FbDivIndex = 0U; + } else if (Best_FeedbackDiv < 30U) { + FbDivIndex = 1U; + } else if (Best_FeedbackDiv < 42U) { + FbDivIndex = 2U; + } + } else if (PllFreq < 20000U) { + PllFreqIndex = 7U; + FbDivIndex = 2U; + if (Best_FeedbackDiv < 20U) { + FbDivIndex = 0U; + /* + * Set PLL spare inputs LSB + */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_SPARE0, 0x577); + } else if (Best_FeedbackDiv < 39U) { + FbDivIndex = 1U; + } + } + + /* + * Enable automatic selection of the VCO, this will work with the + * IP version 2.0.1 and above and using older version of IP is + * not likely to work. + */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_PLL_CRS1, + XRFDC_PLL_VCO_SEL_AUTO_MASK, XRFDC_PLL_VCO_SEL_AUTO_MASK); + + /* + * PLL bits for loop filters LSB + */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_LPF0, + PllTuningMatrix[PllFreqIndex][FbDivIndex][0]); + + /* + * PLL bits for loop filters MSB + */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_LPF1, XRFDC_PLL_LPF1_VAL); + + /* + * Set PLL bits for charge pumps + */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_PLL_CHARGEPUMP, + PllTuningMatrix[PllFreqIndex][FbDivIndex][1]); + } + + CalcSamplingRate = (Best_FeedbackDiv * RefClkFreq) / Best_OutputDiv; + CalcSamplingRate /= XRFDC_MILLI; + + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.SampleRate = + CalcSamplingRate; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.RefClkDivider = RefClkDiv; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.FeedbackDivider = Best_FeedbackDiv; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.OutputDivider = Best_OutputDiv; + } else { + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.SampleRate = + CalcSamplingRate; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.RefClkDivider = RefClkDiv; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.FeedbackDivider = Best_FeedbackDiv; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.OutputDivider = Best_OutputDiv; + } + + Status = XRFDC_SUCCESS; + + return Status; +} +/*****************************************************************************/ +/** +* +* This API is used to get the PLL Configurations. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type represents ADC or DAC. +* @param Tile_Id Valid values are 0-3. +* @param PLLSettings pointer to the XRFdc_PLL_Settings structure to get +* the PLL configurations +* +* @return None +* +******************************************************************************/ +u32 XRFdc_GetPLLConfig(XRFdc *InstancePtr, u32 Type, + u32 Tile_Id, XRFdc_PLL_Settings *PLLSettings) +{ + u32 Status; + u32 BaseAddr; + u16 ReadReg; + double RefClkFreq; + double SampleRate; + u32 FeedbackDivider; + u8 OutputDivider; + u32 RefClkDivider; + u32 Enabled; + u8 DivideMode; + u32 PLLFreq; + u32 PLLFS; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + Xil_AssertNonvoid(PLLSettings != NULL); + + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Tile_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested tile not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_CTRL_STS_BASE(Type, Tile_Id); + PLLFreq = XRFdc_ReadReg(InstancePtr, BaseAddr, XRFDC_PLL_FREQ); + + RefClkFreq = ((double)PLLFreq)/1000; + PLLFS = XRFdc_ReadReg(InstancePtr, BaseAddr, XRFDC_PLL_FS); + SampleRate = ((double)PLLFS)/1000000; + if (PLLFS == 0) { + /*This code is here to support the old IPs.*/ + if (Type == XRFDC_ADC_TILE) { + PLLSettings->Enabled = + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.Enabled; + PLLSettings->FeedbackDivider = + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.FeedbackDivider; + PLLSettings->OutputDivider = + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.OutputDivider; + PLLSettings->RefClkDivider = + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.RefClkDivider; + PLLSettings->RefClkFreq = + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.RefClkFreq; + PLLSettings->SampleRate = + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.SampleRate; + Status = XRFDC_SUCCESS; + goto RETURN_PATH; + } else { + PLLSettings->Enabled = + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.Enabled; + PLLSettings->FeedbackDivider = + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.FeedbackDivider; + PLLSettings->OutputDivider = + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.OutputDivider; + PLLSettings->RefClkDivider = + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.RefClkDivider; + PLLSettings->RefClkFreq = + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.RefClkFreq; + PLLSettings->SampleRate = + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.SampleRate; + Status = XRFDC_SUCCESS; + goto RETURN_PATH; + } + } else { + if (Type == XRFDC_ADC_TILE) { + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id); + } else { + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id); + } + + BaseAddr += XRFDC_HSCOM_ADDR; + + FeedbackDivider = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_PLL_FPDIV, 0x00FF) + 2; + + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, XRFDC_PLL_REFDIV); + if (ReadReg & XRFDC_REFCLK_DIV_1_MASK) { + RefClkDivider = XRFDC_REF_CLK_DIV_1; + } else { + switch (ReadReg & XRFDC_REFCLK_DIV_MASK) { + case XRFDC_REFCLK_DIV_2_MASK: + RefClkDivider = XRFDC_REF_CLK_DIV_2; + break; + case XRFDC_REFCLK_DIV_3_MASK: + RefClkDivider = XRFDC_REF_CLK_DIV_3; + break; + case XRFDC_REFCLK_DIV_4_MASK: + RefClkDivider = XRFDC_REF_CLK_DIV_4; + break; + default: + /* + * IP currently supporting 1 to 4 divider values. This + * error condition might change in future based on IP update. + */ + metal_log(METAL_LOG_ERROR, "\n Unsupported Reference " + "clock Divider value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + } + if (InstancePtr->RFdc_Config.IPType < 2) { + if (XRFdc_GetClockSource(InstancePtr, Type, Tile_Id, &Enabled) + != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + } else { + Enabled = (ReadReg & XRFDC_PLLREFDIV_INPUT_OFF)?XRFDC_DISABLED:XRFDC_ENABLED; + } + + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, XRFDC_PLL_DIVIDER0); + DivideMode = (ReadReg & XRFDC_PLL_DIVIDER0_MODE_MASK) >> XRFDC_PLL_DIVIDER0_SHIFT; + + switch(DivideMode) { + case XRFDC_PLL_OUTDIV_MODE_1: + OutputDivider = 1; + break; + case XRFDC_PLL_OUTDIV_MODE_2: + OutputDivider = 2; + break; + case XRFDC_PLL_OUTDIV_MODE_3: + OutputDivider = 3; + break; + case XRFDC_PLL_OUTDIV_MODE_N: + OutputDivider = ((ReadReg & XRFDC_PLL_DIVIDER0_VALUE_MASK) + 2) << 1; + break; + default: + metal_log(METAL_LOG_ERROR, "\n Unsupported Output " + "clock Divider value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + break; + } + PLLSettings->Enabled = Enabled; + PLLSettings->FeedbackDivider = FeedbackDivider; + PLLSettings->OutputDivider = OutputDivider; + PLLSettings->RefClkDivider = RefClkDivider; + PLLSettings->RefClkFreq = RefClkFreq; + PLLSettings->SampleRate = SampleRate; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + + return Status; +} + +/*****************************************************************************/ +/** +* +* This function used for dynamically switch between internal PLL and +* external clcok source and configuring the internal PLL +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type indicates ADC/DAC +* @param Tile_Id indicates Tile number (0-3) +* @param Source Clock source internal PLL or external clock source +* @param RefClkFreq Reference Clock Frequency in MHz(102.40625MHz - 1.2GHz) +* @param SamplingRate Sampling Rate in MHz(0.1- 6.554GHz for DAC and +* 0.5/1.0 - 2.058/4.116GHz for ADC based on the device package). +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Tile not enabled. +* +* @note This API enables automatic selection of the VCO which will work in +* IP version 2.0.1 and above. Using older version of IP this API is +* not likely to work. +* +******************************************************************************/ +u32 XRFdc_DynamicPLLConfig(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 Source, double RefClkFreq, double SamplingRate) +{ + u32 ClkSrc = 0U; + u32 Status; + u32 BaseAddr; + u32 PLLEnable = 0x0; + u32 InitialPowerUpState; + double MaxSampleRate; + double MinSampleRate; + u32 PLLFreq; + u32 PLLFS; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if ((Source != XRFDC_INTERNAL_PLL_CLK) && + (Source != XRFDC_EXTERNAL_CLK)) { + metal_log(METAL_LOG_ERROR, "\n Invalid Source " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Tile_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested tile not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + /* + * Get Tile clock source information + */ + if (XRFdc_GetClockSource(InstancePtr, Type, Tile_Id, &ClkSrc) + != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + if (XRFdc_GetMaxSampleRate(InstancePtr, Type, Tile_Id, &MaxSampleRate) != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (XRFdc_GetMinSampleRate(InstancePtr, Type, Tile_Id, &MinSampleRate) != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((SamplingRate < MinSampleRate) || + (SamplingRate > MaxSampleRate)) { + metal_log(METAL_LOG_ERROR, "\n Invalid sampling " + "rate value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + BaseAddr = XRFDC_CTRL_STS_BASE(Type, Tile_Id); + + if (Source == XRFDC_INTERNAL_PLL_CLK) { + if ((RefClkFreq < XRFDC_REFFREQ_MIN) || + (RefClkFreq > XRFDC_REFFREQ_MAX)) { + metal_log(METAL_LOG_ERROR, "\n Input reference clock " + "frequency does not respect the specifications " + "for internal PLL usage. Please use a different " + "frequency or bypass the internal PLL", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + } + + PLLFreq = (u32)(RefClkFreq*1000); + PLLFS = (u32)(SamplingRate*1000); + XRFdc_WriteReg(InstancePtr, BaseAddr, XRFDC_PLL_FREQ, PLLFreq); + XRFdc_WriteReg(InstancePtr, BaseAddr, XRFDC_PLL_FS, PLLFS); + + if ((Source != XRFDC_INTERNAL_PLL_CLK) && + (ClkSrc != XRFDC_INTERNAL_PLL_CLK)) { + metal_log(METAL_LOG_DEBUG, "\n Requested Tile %d " + "uses external clock source in %s\r\n", Tile_Id, __func__); + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.SampleRate = + (double)(SamplingRate/1000); + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.RefClkFreq = RefClkFreq; + } else { + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.SampleRate = + (double)(SamplingRate/1000); + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.RefClkFreq = RefClkFreq; + } + Status = XRFDC_SUCCESS; + goto RETURN_PATH; + } + + if (Type == XRFDC_ADC_TILE) { + BaseAddr = XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id); + InitialPowerUpState = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_STATUS_OFFSET, XRFDC_PWR_UP_STAT_MASK) >> XRFDC_PWR_UP_STAT_SHIFT; + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + XRFDC_HSCOM_ADDR; + } else { + BaseAddr = XRFDC_DAC_TILE_CTRL_STATS_ADDR(Tile_Id); + InitialPowerUpState = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_STATUS_OFFSET, XRFDC_PWR_UP_STAT_MASK) >> XRFDC_PWR_UP_STAT_SHIFT; + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id) + XRFDC_HSCOM_ADDR; + } + + /* + * Stop the ADC or DAC tile by putting tile in reset state if not stopped already + */ + if (InitialPowerUpState != XRFDC_DISABLED) { + Status = XRFdc_Shutdown(InstancePtr, Type, Tile_Id) ; + if (Status != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + } + if (Source == XRFDC_INTERNAL_PLL_CLK) { + PLLEnable = 0x1; + /* + * Configure the PLL + */ + if (XRFdc_SetPLLConfig(InstancePtr, Type, Tile_Id, RefClkFreq, + SamplingRate) != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_CLK_NETWORK_CTRL1, XRFDC_CLK_NETWORK_CTRL1_USE_PLL_MASK, + XRFDC_CLK_NETWORK_CTRL1_USE_PLL_MASK); + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_HSCOM_PWR_STATE_OFFSET, + XRFDC_HSCOM_PWR_STATS_PLL); + } else { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_CLK_NETWORK_CTRL1, + XRFDC_CLK_NETWORK_CTRL1_USE_PLL_MASK, 0x0); + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_HSCOM_PWR_STATE_OFFSET, + XRFDC_HSCOM_PWR_STATS_EXTERNAL); + SamplingRate /= XRFDC_MILLI; + + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.SampleRate = + SamplingRate; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.RefClkDivider = 0x0U; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.FeedbackDivider = 0x0U; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.OutputDivider = 0x0U; + } else { + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.SampleRate = + SamplingRate; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.RefClkDivider = 0x0U; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.FeedbackDivider = 0x0U; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.OutputDivider = 0x0U; + } + } + + /* + * Re-start the ADC or DAC tile if tile was shut down in this function + */ + if (InitialPowerUpState != XRFDC_DISABLED) { + Status = XRFdc_StartUp(InstancePtr, Type, Tile_Id) ; + if (Status != XRFDC_SUCCESS) { + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + } + + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.RefClkFreq = RefClkFreq; + InstancePtr->ADC_Tile[Tile_Id].PLL_Settings.Enabled = PLLEnable; + } else { + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.RefClkFreq = RefClkFreq; + InstancePtr->DAC_Tile[Tile_Id].PLL_Settings.Enabled = PLLEnable; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/** @} */ diff --git a/mpm/lib/rfdc/xrfdc_g.c b/mpm/lib/rfdc/xrfdc_g.c new file mode 100644 index 000000000..857fb1ffd --- /dev/null +++ b/mpm/lib/rfdc/xrfdc_g.c @@ -0,0 +1,619 @@ +/******************************************************************* +* Copyright (C) 2017-2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* + +*******************************************************************************/ + +/*****************************************************************************/ +/** +* +* @file xrfdc_g.c +* @addtogroup rfdc_v6_0 +* @{ +* +* This file contains a configuration table that specifies the configuration of +* RFdc devices in the system. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.0 sk 05/16/17 Initial release +* 5.1 cog 01/29/19 Added FSMax, NumSlice & IP_Type. +* +* </pre> +* +******************************************************************************/ +#ifdef __BAREMETAL__ +/***************************** Include Files ********************************/ +#include "xparameters.h" +#include "mpm/rfdc/xrfdc.h" +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Variable Definitions ****************************/ +/** + * The configuration table for devices + */ + +XRFdc_Config XRFdc_ConfigTable[XPAR_XRFDC_NUM_INSTANCES] = +{ + { + XPAR_USP_RF_DATA_CONVERTER_0_DEVICE_ID, + XPAR_USP_RF_DATA_CONVERTER_0_BASEADDR, + XPAR_USP_RF_DATA_CONVERTER_0_HIGH_SPEED_ADC, + XPAR_USP_RF_DATA_CONVERTER_0_SYSREF_MASTER, + XPAR_USP_RF_DATA_CONVERTER_0_SYSREF_MASTER, + XPAR_USP_RF_DATA_CONVERTER_0_SYSREF_SOURCE, + XPAR_USP_RF_DATA_CONVERTER_0_SYSREF_SOURCE, + XPAR_USP_RF_DATA_CONVERTER_0_IP_TYPE, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_PLL_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_SAMPLING_RATE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_REFCLK_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_FABRIC_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_FBDIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_OUTDIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_REFCLK_DIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_BAND, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_FS_MAX, + XPAR_USP_RF_DATA_CONVERTER_0_DAC0_SLICES, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE00_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL00, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE00, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE00, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE01_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL01, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE01, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE01, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE02_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL02, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE02, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE02, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE03_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL03, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE03, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE03, + }, + }, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE00, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH00, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE00, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO00_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER00_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE00, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE01, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH01, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE01, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO01_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER01_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE01, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE02, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH02, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE02, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO02_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER02_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE02, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE03, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH03, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE03, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO03_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER03_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE03, + }, + }, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_PLL_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_SAMPLING_RATE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_REFCLK_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_FABRIC_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_FBDIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_OUTDIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_REFCLK_DIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_BAND, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_FS_MAX, + XPAR_USP_RF_DATA_CONVERTER_0_DAC1_SLICES, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE10_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL10, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE10, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE10, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE11_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL11, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE11, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE11, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE12_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL12, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE12, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE12, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE13_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL13, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE13, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE13, + }, + }, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE10, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH10, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE10, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO10_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER10_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE10, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE11, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH11, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE11, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO11_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER11_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE11, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE12, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH12, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE12, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO12_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER12_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE12, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE13, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH13, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE13, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO13_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER13_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE13, + }, + }, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_PLL_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_SAMPLING_RATE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_REFCLK_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_FABRIC_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_FBDIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_OUTDIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_REFCLK_DIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_BAND, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_FS_MAX, + XPAR_USP_RF_DATA_CONVERTER_0_DAC2_SLICES, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE20_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL20, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE20, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE20, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE21_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL21, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE21, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE21, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE22_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL22, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE22, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE22, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE23_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL23, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE23, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE23, + }, + }, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE20, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH20, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE20, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO20_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER20_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE20, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE21, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH21, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE21, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO21_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER21_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE21, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE22, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH22, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE22, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO22_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER22_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE22, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE23, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH23, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE23, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO23_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER23_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE23, + }, + }, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_PLL_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_SAMPLING_RATE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_REFCLK_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_FABRIC_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_FBDIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_OUTDIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_REFCLK_DIV, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_BAND, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_FS_MAX, + XPAR_USP_RF_DATA_CONVERTER_0_DAC3_SLICES, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE30_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL30, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE30, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE30, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE31_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL31, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE31, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE31, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE32_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL32, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE32, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE32, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_SLICE33_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INVSINC_CTRL33, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_MODE33, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DECODER_MODE33, + }, + }, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE30, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH30, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE30, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO30_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER30_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE30, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE31, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH31, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE31, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO31_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER31_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE31, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE32, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH32, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE32, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO32_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER32_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE32, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_TYPE33, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_DATA_WIDTH33, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_INTERPOLATION_MODE33, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_FIFO33_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_ADDER33_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_DAC_MIXER_TYPE33, + }, + }, + }, + }, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_PLL_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_SAMPLING_RATE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_REFCLK_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_FABRIC_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_FBDIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_OUTDIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_REFCLK_DIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_BAND, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_FS_MAX, + XPAR_USP_RF_DATA_CONVERTER_0_ADC0_SLICES, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE00_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE00, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE01_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE01, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE02_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE02, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE03_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE03, + }, + }, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE00, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH00, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE00, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO00_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE00, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE01, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH01, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE01, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO01_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE01, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE02, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH02, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE02, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO02_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE02, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE03, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH03, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE03, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO03_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE03, + }, + }, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_PLL_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_SAMPLING_RATE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_REFCLK_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_FABRIC_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_FBDIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_OUTDIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_REFCLK_DIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_BAND, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_FS_MAX, + XPAR_USP_RF_DATA_CONVERTER_0_ADC1_SLICES, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE10_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE10, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE11_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE11, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE12_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE12, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE13_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE13, + }, + }, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE10, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH10, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE10, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO10_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE10, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE11, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH11, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE11, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO11_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE11, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE12, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH12, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE12, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO12_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE12, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE13, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH13, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE13, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO13_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE13, + }, + }, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_PLL_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_SAMPLING_RATE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_REFCLK_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_FABRIC_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_FBDIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_OUTDIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_REFCLK_DIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_BAND, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_FS_MAX, + XPAR_USP_RF_DATA_CONVERTER_0_ADC2_SLICES, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE20_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE20, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE21_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE21, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE22_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE22, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE23_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE23, + }, + }, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE20, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH20, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE20, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO20_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE20, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE21, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH21, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE21, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO21_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE21, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE22, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH22, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE22, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO22_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE22, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE23, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH23, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE23, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO23_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE23, + }, + }, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_PLL_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_SAMPLING_RATE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_REFCLK_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_FABRIC_FREQ, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_FBDIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_OUTDIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_REFCLK_DIV, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_BAND, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_FS_MAX, + XPAR_USP_RF_DATA_CONVERTER_0_ADC3_SLICES, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE30_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE30, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE31_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE31, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE32_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE32, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_SLICE33_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_MODE33, + }, + }, + { + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE30, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH30, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE30, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO30_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE30, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE31, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH31, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE31, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO31_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE31, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE32, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH32, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE32, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO32_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE32, + }, + { + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_TYPE33, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DATA_WIDTH33, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_DECIMATION_MODE33, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_FIFO33_ENABLE, + XPAR_USP_RF_DATA_CONVERTER_0_ADC_MIXER_TYPE33, + }, + }, + }, + } + } +}; +#endif +/** @} */ diff --git a/mpm/lib/rfdc/xrfdc_intr.c b/mpm/lib/rfdc/xrfdc_intr.c new file mode 100644 index 000000000..7b8bd0927 --- /dev/null +++ b/mpm/lib/rfdc/xrfdc_intr.c @@ -0,0 +1,771 @@ +/****************************************************************************** +* +* Copyright (C) 2017-2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc_intr.c +* @addtogroup rfdc_v6_0 +* @{ +* +* This file contains functions related to RFdc interrupt handling. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- ----- -------- ----------------------------------------------- +* 1.0 sk 05/16/17 First release +* 2.1 sk 09/15/17 Remove Libmetal library dependency for MB. +* 09/18/17 Add API to clear the interrupts. +* sk 09/21/17 Add support for Over voltage and Over +* Range interrupts. +* 2.2 sk 10/18/17 Add support for FIFO and DATA overflow interrupt +* 5.0 sk 08/24/18 Reorganize the code to improve readability and +* optimization. +* 5.1 cog 01/29/19 Replace structure reference ADC checks with +* function. +* 6.0 cog 02/20/19 Added handling for new ADC common mode over/under +* voltage interrupts. +* cog 02/20/19 XRFdc_GetIntrStatus now populates a pointer with the +* status and returns an error code. +* cog 02/20/19 XRFdc_IntrClr, XRFdc_IntrDisable and XRFdc_IntrEnable +* now return error codes. +* cog 03/25/19 The new common mode over/under voltage interrupts mask +* bits were clashing with other interrupt bits. +* </pre> +* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "mpm/rfdc/xrfdc.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions ****************************/ + +/****************************************************************************/ +/** +* +* This function sets the interrupt mask. +* +* @param InstancePtr is a pointer to the XRFdc instance +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param IntrMask contains the interrupts to be enabled. +* '1' enables an interrupt, and '0' disables. +* +* @return - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not available. +* +* @note None. +* +*****************************************************************************/ +u32 XRFdc_IntrEnable(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 IntrMask) +{ + u32 BaseAddr; + u32 ReadReg; + u32 Index; + u32 NoOfBlocks; + u32 Status; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + Index = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + ReadReg = XRFdc_ReadReg16(InstancePtr, 0x0, + XRFDC_COMMON_INTR_ENABLE); + if (Type == XRFDC_ADC_TILE) { + ReadReg |= (1U << (Tile_Id + 4)); + XRFdc_WriteReg16(InstancePtr, 0x0, + XRFDC_COMMON_INTR_ENABLE, ReadReg); + BaseAddr = XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_INTR_ENABLE, + (1U << Index), (1U << Index)); + /* Enable Converter interrupts */ + ReadReg = XRFdc_ReadReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_EN(Index)); + if ((IntrMask & XRFDC_ADC_OVR_VOLTAGE_MASK) != 0U) { + ReadReg |= (XRFDC_ADC_OVR_VOLTAGE_MASK >> + XRFDC_ADC_OVR_VOL_RANGE_SHIFT); + } + if ((IntrMask & XRFDC_ADC_OVR_RANGE_MASK) != 0U) { + ReadReg |= (XRFDC_ADC_OVR_RANGE_MASK >> + XRFDC_ADC_OVR_VOL_RANGE_SHIFT); + } + if ((IntrMask & XRFDC_ADC_FIFO_OVR_MASK) != 0U) { + ReadReg |= + (XRFDC_ADC_FIFO_OVR_MASK >> XRFDC_ADC_DAT_FIFO_OVR_SHIFT); + } + if ((IntrMask & XRFDC_ADC_DAT_OVR_MASK) != 0U) { + ReadReg |= + (XRFDC_ADC_DAT_OVR_MASK >> XRFDC_ADC_DAT_FIFO_OVR_SHIFT); + } + if ((IntrMask & XRFDC_ADC_CMODE_OVR_MASK) != 0U) { + ReadReg |= + (XRFDC_ADC_CMODE_OVR_MASK >> XRFDC_ADC_CMODE_SHIFT); + } + if ((IntrMask & XRFDC_ADC_CMODE_UNDR_MASK) != 0U) { + ReadReg |= + (XRFDC_ADC_CMODE_UNDR_MASK >> XRFDC_ADC_CMODE_SHIFT); + } + + XRFdc_WriteReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_EN(Index), ReadReg); + + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Index); + + /* Check for FIFO interface interrupts */ + if ((IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK) != 0U) { + ReadReg = (IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_FABRIC_IMR_OFFSET, + XRFDC_IXR_FIFOUSRDAT_MASK, ReadReg); + } + /* Check for SUBADC interrupts */ + if ((IntrMask & XRFDC_SUBADC_IXR_DCDR_MASK) != 0U) { + ReadReg = (IntrMask & XRFDC_SUBADC_IXR_DCDR_MASK) >> + XRFDC_ADC_SUBADC_DCDR_SHIFT; + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_DEC_IMR_OFFSET, + XRFDC_DEC_IMR_MASK, ReadReg); + } + /* Check for DataPath interrupts */ + if ((IntrMask & XRFDC_ADC_IXR_DATAPATH_MASK) != 0U) { + ReadReg = (IntrMask & XRFDC_ADC_IXR_DATAPATH_MASK) >> + XRFDC_DATA_PATH_SHIFT; + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_DATPATH_IMR_OFFSET, + XRFDC_ADC_DAT_IMR_MASK, ReadReg); + } + } else { + ReadReg |= (1U << Tile_Id); + XRFdc_WriteReg16(InstancePtr, 0x0, + XRFDC_COMMON_INTR_ENABLE, ReadReg); + BaseAddr = XRFDC_DAC_TILE_CTRL_STATS_ADDR(Tile_Id); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, + XRFDC_INTR_ENABLE, (1U << Index), (1U << Index)); + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Index); + /* Check for FIFO interface interrupts */ + if ((IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK) != 0U) { + ReadReg = (IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_DAC_FABRIC_IMR_OFFSET, + XRFDC_IXR_FIFOUSRDAT_MASK, ReadReg); + } + + if ((IntrMask & XRFDC_DAC_IXR_DATAPATH_MASK) != 0U) { + ReadReg = (IntrMask & XRFDC_DAC_IXR_DATAPATH_MASK) >> + XRFDC_DATA_PATH_SHIFT; + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_DATPATH_IMR_OFFSET, + XRFDC_DAC_DAT_IMR_MASK, ReadReg); + } + } + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/****************************************************************************/ +/** +* +* This function clears the interrupt mask. +* +* @param InstancePtr is a pointer to the XRFdc instance +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param IntrMask contains the interrupts to be disabled. +* '1' disables an interrupt, and '0' remains no change. +* +* @return - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not available. +* +* @note None. +* +*****************************************************************************/ +u32 XRFdc_IntrDisable(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 IntrMask) +{ + u32 BaseAddr; + u32 ReadReg; + u32 Status; + u32 Index; + u32 NoOfBlocks; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + Index = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + if (Type == XRFDC_ADC_TILE) { + BaseAddr = XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id); + /* Check for Over Voltage and Over Range */ + ReadReg = XRFdc_ReadReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_EN(Index)); + if ((IntrMask & XRFDC_ADC_OVR_VOLTAGE_MASK) != 0U) { + ReadReg &= ~(XRFDC_ADC_OVR_VOLTAGE_MASK >> + XRFDC_ADC_OVR_VOL_RANGE_SHIFT); + } + if ((IntrMask & XRFDC_ADC_OVR_RANGE_MASK) != 0U) { + ReadReg &= ~(XRFDC_ADC_OVR_RANGE_MASK >> + XRFDC_ADC_OVR_VOL_RANGE_SHIFT); + } + /* Disable Converter interrupts */ + if ((IntrMask & XRFDC_ADC_FIFO_OVR_MASK) != 0U) { + ReadReg &= + ~(XRFDC_ADC_FIFO_OVR_MASK >> XRFDC_ADC_DAT_FIFO_OVR_SHIFT); + } + if ((IntrMask & XRFDC_ADC_DAT_OVR_MASK) != 0U) { + ReadReg &= + ~(XRFDC_ADC_DAT_OVR_MASK >> XRFDC_ADC_DAT_FIFO_OVR_SHIFT); + } + if ((IntrMask & XRFDC_ADC_CMODE_OVR_MASK) != 0U) { + ReadReg &= + ~(XRFDC_ADC_CMODE_OVR_MASK >> XRFDC_ADC_CMODE_SHIFT); + } + if ((IntrMask & XRFDC_ADC_CMODE_UNDR_MASK) != 0U) { + ReadReg &= + ~(XRFDC_ADC_CMODE_UNDR_MASK >> XRFDC_ADC_CMODE_SHIFT); + } + + XRFdc_WriteReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_EN(Index), ReadReg); + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Index); + /* Check for FIFO interface interrupts */ + if ((IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK) != 0U) { + ReadReg = IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK; + XRFdc_ClrReg(InstancePtr, BaseAddr, XRFDC_ADC_FABRIC_IMR_OFFSET, + ReadReg); + } + /* Check for SUBADC interrupts */ + if ((IntrMask & XRFDC_SUBADC_IXR_DCDR_MASK) != 0U) { + ReadReg = ((IntrMask & XRFDC_SUBADC_IXR_DCDR_MASK) >> + XRFDC_ADC_SUBADC_DCDR_SHIFT); + XRFdc_ClrReg(InstancePtr, BaseAddr, XRFDC_ADC_DEC_IMR_OFFSET, + ReadReg); + } + /* Check for DataPath interrupts */ + if ((IntrMask & XRFDC_ADC_IXR_DATAPATH_MASK) != 0U) { + ReadReg = ((IntrMask & + XRFDC_ADC_IXR_DATAPATH_MASK) >> XRFDC_DATA_PATH_SHIFT); + XRFdc_ClrReg(InstancePtr, BaseAddr, XRFDC_DATPATH_IMR_OFFSET, + ReadReg); + } + } else { + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Index); + /* Check for FIFO interface interrupts */ + if ((IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK) != 0U) { + ReadReg = (IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK); + XRFdc_ClrReg(InstancePtr, BaseAddr, XRFDC_DAC_FABRIC_IMR_OFFSET, + ReadReg); + } + /* Check for FIFO DataPath interrupts */ + if ((IntrMask & XRFDC_DAC_IXR_DATAPATH_MASK) != 0U) { + ReadReg = ((IntrMask & + XRFDC_DAC_IXR_DATAPATH_MASK) >> XRFDC_DATA_PATH_SHIFT); + XRFdc_ClrReg(InstancePtr, BaseAddr, XRFDC_DATPATH_IMR_OFFSET, + ReadReg); + } + } + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/****************************************************************************/ +/** +* +* This function returns the interrupt status read from Interrupt Status +* Register(ISR). +* +* @param InstancePtr is a pointer to the XRFdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param IntrStsPtr is pointer to a32-bit value representing the contents of +* the Interrupt Status Registers (FIFO interface, Decoder interface, +* Data Path Interface). +* +* @return - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not available. +* +* @note None. +* +*****************************************************************************/ +u32 XRFdc_GetIntrStatus(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 *IntrStsPtr) +{ + u32 BaseAddr; + u32 ReadReg; + u32 Status; + u32 Block; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(IntrStsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Block = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + if ((Block_Id == XRFDC_BLK_ID2) || (Block_Id == XRFDC_BLK_ID3)) { + Block = XRFDC_BLK_ID1; + } + if (Block_Id == XRFDC_BLK_ID1) { + Block = XRFDC_BLK_ID0; + } + } + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + *IntrStsPtr = 0; + + if (Type == XRFDC_ADC_TILE) { + BaseAddr = XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id); + /* Check for Over Voltage and Over Range */ + ReadReg = XRFdc_ReadReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_STS(Block_Id)); + *IntrStsPtr |= ((ReadReg & XRFDC_INTR_OVR_VOLTAGE_MASK) << + XRFDC_ADC_OVR_VOL_RANGE_SHIFT); + *IntrStsPtr |= ((ReadReg & XRFDC_INTR_OVR_RANGE_MASK) << + XRFDC_ADC_OVR_VOL_RANGE_SHIFT); + *IntrStsPtr |= ((ReadReg & XRFDC_INTR_FIFO_OVR_MASK) << + XRFDC_ADC_DAT_FIFO_OVR_SHIFT); + *IntrStsPtr |= ((ReadReg & XRFDC_INTR_DAT_OVR_MASK) << + XRFDC_ADC_DAT_FIFO_OVR_SHIFT); + + /* Check for Common Mode Over/Under Voltage */ + *IntrStsPtr |= ((ReadReg & XRFDC_INTR_CMODE_OVR_MASK) << + XRFDC_ADC_CMODE_SHIFT); + *IntrStsPtr |= ((ReadReg & XRFDC_INTR_CMODE_UNDR_MASK) << + XRFDC_ADC_CMODE_SHIFT); + + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Block_Id); + /* Check for FIFO interface interrupts */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_FABRIC_ISR_OFFSET); + *IntrStsPtr |= (ReadReg & XRFDC_IXR_FIFOUSRDAT_MASK); + /* Check for SUBADC interrupts */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_DEC_ISR_OFFSET); + *IntrStsPtr |= ((ReadReg & XRFDC_DEC_ISR_SUBADC_MASK) << + XRFDC_ADC_SUBADC_DCDR_SHIFT); + /* Check for DataPath interrupts */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_DATPATH_ISR_OFFSET); + *IntrStsPtr |= ((ReadReg & XRFDC_ADC_DAT_PATH_ISR_MASK) << + XRFDC_DATA_PATH_SHIFT); + } else { + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Block_Id); + /* Check for FIFO interface interrupts */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_DAC_FABRIC_ISR_OFFSET); + *IntrStsPtr |= (ReadReg & XRFDC_IXR_FIFOUSRDAT_MASK); + /* Check for DataPath interrupts */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_DATPATH_ISR_OFFSET); + *IntrStsPtr |= ((ReadReg & XRFDC_DAC_DAT_PATH_ISR_MASK) << + XRFDC_DATA_PATH_SHIFT); + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/****************************************************************************/ +/** +* +* This function clear the interrupts. +* +* @param InstancePtr is a pointer to the XRFdc instance +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3, and -1. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param IntrMask contains the interrupts to be cleared. +* +* @return - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not available. +* +* @note None. +* +*****************************************************************************/ +u32 XRFdc_IntrClr(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, u32 IntrMask) +{ + u32 BaseAddr; + u32 Status; + u32 Block; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Block = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + if ((Block_Id == XRFDC_BLK_ID2) || (Block_Id == XRFDC_BLK_ID3)) { + Block = XRFDC_BLK_ID1; + } + if (Block_Id == XRFDC_BLK_ID1) { + Block = XRFDC_BLK_ID0; + } + } + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, Block); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + + if (Type == XRFDC_ADC_TILE) { + /* ADC */ + BaseAddr = XRFDC_ADC_TILE_CTRL_STATS_ADDR(Tile_Id); + /* Check for Converter interrupts */ + if ((IntrMask & XRFDC_ADC_OVR_VOLTAGE_MASK) != 0U) { + XRFdc_WriteReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_STS(Block_Id), (IntrMask & + XRFDC_ADC_OVR_VOLTAGE_MASK) >> XRFDC_ADC_OVR_VOL_RANGE_SHIFT); + } + if ((IntrMask & XRFDC_ADC_OVR_RANGE_MASK) != 0U) { + XRFdc_WriteReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_STS(Block_Id), (IntrMask & + XRFDC_ADC_OVR_RANGE_MASK) >> XRFDC_ADC_OVR_VOL_RANGE_SHIFT); + } + if ((IntrMask & XRFDC_ADC_FIFO_OVR_MASK) != 0U) { + XRFdc_WriteReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_STS(Block_Id), (IntrMask & + XRFDC_ADC_FIFO_OVR_MASK) >> XRFDC_ADC_DAT_FIFO_OVR_SHIFT); + } + if ((IntrMask & XRFDC_ADC_DAT_OVR_MASK) != 0U) { + XRFdc_WriteReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_STS(Block_Id), (IntrMask & + XRFDC_ADC_DAT_OVR_MASK) >> XRFDC_ADC_DAT_FIFO_OVR_SHIFT); + } + if ((IntrMask & XRFDC_ADC_CMODE_OVR_MASK) != 0U) { + XRFdc_WriteReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_STS(Block_Id), (IntrMask & + XRFDC_ADC_CMODE_OVR_MASK) >> XRFDC_ADC_CMODE_SHIFT); + } + if ((IntrMask & XRFDC_ADC_CMODE_UNDR_MASK) != 0U) { + XRFdc_WriteReg(InstancePtr, BaseAddr, + XRFDC_CONV_INTR_STS(Block_Id), (IntrMask & + XRFDC_ADC_CMODE_UNDR_MASK) >> XRFDC_ADC_CMODE_SHIFT); + } + BaseAddr = XRFDC_ADC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Block_Id); + /* Check for FIFO interface interrupts */ + if ((IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK) != 0U) { + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_FABRIC_ISR_OFFSET, + (IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK)); + } + /* Check for SUBADC interrupts */ + if ((IntrMask & XRFDC_SUBADC_IXR_DCDR_MASK) != 0U) { + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_DEC_ISR_OFFSET, (u16)((IntrMask & + XRFDC_SUBADC_IXR_DCDR_MASK) >> XRFDC_ADC_SUBADC_DCDR_SHIFT)); + } + /* Check for DataPath interrupts */ + if ((IntrMask & XRFDC_ADC_IXR_DATAPATH_MASK) != 0U) { + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_DATPATH_ISR_OFFSET, (u16)(IntrMask & + XRFDC_ADC_IXR_DATAPATH_MASK) >> XRFDC_DATA_PATH_SHIFT); + } + } else { + /* DAC */ + BaseAddr = XRFDC_DAC_TILE_DRP_ADDR(Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Block_Id); + /* Check for FIFO interface interrupts */ + if ((IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK) != 0U) { + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_DAC_FABRIC_ISR_OFFSET, + (u16)(IntrMask & XRFDC_IXR_FIFOUSRDAT_MASK)); + } + /* Check for DataPath interrupts */ + if ((IntrMask & XRFDC_DAC_IXR_DATAPATH_MASK) != 0U) { + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_DATPATH_ISR_OFFSET, (u16)(IntrMask & + XRFDC_DAC_IXR_DATAPATH_MASK) >> XRFDC_DATA_PATH_SHIFT); + } + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} + +/****************************************************************************/ +/** +* +* This function is the interrupt handler for the driver. +* It must be connected to an interrupt system by the application such that it +* can be called when an interrupt occurs. +* +* @param Vector is interrupt vector number. Libmetal status handler +* expects two parameters in the handler prototype, hence +* kept this parameter. This is not used inside +* the interrupt handler API. +* @param XRFdcPtr contains a pointer to the driver instance +* +* @return None. +* +* @note Vector param is not useful inside the interrupt handler, hence +* typecast with void to remove compilation warning. +* +******************************************************************************/ +u32 XRFdc_IntrHandler(u32 Vector, void *XRFdcPtr) +{ + XRFdc *InstancePtr = (XRFdc *)XRFdcPtr; + u32 Intrsts = 0x0U; + u32 Tile_Id = XRFDC_TILE_ID4; + u32 Block_Id = XRFDC_BLK_ID4; + u32 ReadReg; + u16 Type = 0U; + u32 BaseAddr; + u32 IntrMask = 0x0U; + u32 Block; + + Xil_AssertNonvoid(InstancePtr != NULL); + + (void)Vector; + /* + * Read the interrupt ID register to determine which + * interrupt is active + */ + ReadReg = XRFdc_ReadReg16(InstancePtr, 0x0, + XRFDC_COMMON_INTR_STS); + if ((ReadReg & XRFDC_EN_INTR_DAC_TILE0_MASK) != 0U) { + Type = XRFDC_DAC_TILE; + Tile_Id = XRFDC_TILE_ID0; + } else if ((ReadReg & XRFDC_EN_INTR_DAC_TILE1_MASK) != 0U) { + Type = XRFDC_DAC_TILE; + Tile_Id = XRFDC_TILE_ID1; + } else if ((ReadReg & XRFDC_EN_INTR_DAC_TILE2_MASK) != 0U) { + Type = XRFDC_DAC_TILE; + Tile_Id = XRFDC_TILE_ID2; + } else if ((ReadReg & XRFDC_EN_INTR_DAC_TILE3_MASK) != 0U) { + Type = XRFDC_DAC_TILE; + Tile_Id = XRFDC_TILE_ID3; + } else if ((ReadReg & XRFDC_EN_INTR_ADC_TILE0_MASK) != 0U) { + Type = XRFDC_ADC_TILE; + Tile_Id = XRFDC_TILE_ID0; + } else if ((ReadReg & XRFDC_EN_INTR_ADC_TILE1_MASK) != 0U) { + Type = XRFDC_ADC_TILE; + Tile_Id = XRFDC_TILE_ID1; + } else if ((ReadReg & XRFDC_EN_INTR_ADC_TILE2_MASK) != 0U) { + Type = XRFDC_ADC_TILE; + Tile_Id = XRFDC_TILE_ID2; + } else if ((ReadReg & XRFDC_EN_INTR_ADC_TILE3_MASK) != 0U) { + Type = XRFDC_ADC_TILE; + Tile_Id = XRFDC_TILE_ID3; + } else { + metal_log(METAL_LOG_DEBUG, "\n Invalid Tile_Id \r\n"); + } + + BaseAddr = XRFDC_CTRL_STS_BASE(Type, Tile_Id); + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, XRFDC_INTR_STS); + if ((ReadReg & XRFDC_EN_INTR_SLICE0_MASK) != 0U) { + Block_Id = XRFDC_BLK_ID0; + } else if ((ReadReg & XRFDC_EN_INTR_SLICE1_MASK) != 0U) { + Block_Id = XRFDC_BLK_ID1; + } else if ((ReadReg & XRFDC_EN_INTR_SLICE2_MASK) != 0U) { + Block_Id = XRFDC_BLK_ID2; + } else if ((ReadReg & XRFDC_EN_INTR_SLICE3_MASK) != 0U) { + Block_Id = XRFDC_BLK_ID3; + } else { + metal_log(METAL_LOG_DEBUG, "\n Invalid ADC Block_Id \r\n"); + } + (void)XRFdc_GetIntrStatus(InstancePtr, Type, Tile_Id, Block_Id, &Intrsts); + if (Type == XRFDC_ADC_TILE) { + /* ADC */ + if ((Intrsts & XRFDC_ADC_OVR_VOLTAGE_MASK) != 0U) { + IntrMask |= Intrsts & XRFDC_ADC_OVR_VOLTAGE_MASK; + metal_log(METAL_LOG_DEBUG, "\n ADC Over Voltage interrupt \r\n"); + } + if ((Intrsts & XRFDC_ADC_OVR_RANGE_MASK) != 0U) { + IntrMask |= Intrsts & XRFDC_ADC_OVR_RANGE_MASK; + metal_log(METAL_LOG_DEBUG, "\n ADC Over Range interrupt \r\n"); + } + if ((Intrsts & XRFDC_ADC_FIFO_OVR_MASK) != 0U) { + IntrMask |= Intrsts & XRFDC_ADC_FIFO_OVR_MASK; + metal_log(METAL_LOG_DEBUG, "\n ADC FIFO OF interrupt \r\n"); + } + if ((Intrsts & XRFDC_ADC_DAT_OVR_MASK) != 0U) { + IntrMask |= Intrsts & XRFDC_ADC_DAT_OVR_MASK; + metal_log(METAL_LOG_DEBUG, "\n ADC DATA OF interrupt \r\n"); + } + if ((Intrsts & XRFDC_ADC_CMODE_OVR_MASK) != 0U) { + IntrMask |= XRFDC_ADC_CMODE_OVR_MASK; + metal_log(METAL_LOG_DEBUG, "\n ADC CMODE OV interrupt \r\n"); + } + if ((Intrsts & XRFDC_ADC_CMODE_UNDR_MASK) != 0U) { + IntrMask |= XRFDC_ADC_CMODE_UNDR_MASK; + metal_log(METAL_LOG_DEBUG, "\n ADC CMODE UV interrupt \r\n"); + } + if ((Intrsts & XRFDC_IXR_FIFOUSRDAT_MASK) != 0U) { + IntrMask |= Intrsts & XRFDC_IXR_FIFOUSRDAT_MASK; + metal_log(METAL_LOG_DEBUG, "\n ADC FIFO interface interrupt \r\n"); + } + if ((Intrsts & XRFDC_SUBADC_IXR_DCDR_MASK) != 0U) { + IntrMask |= Intrsts & XRFDC_SUBADC_IXR_DCDR_MASK; + metal_log(METAL_LOG_DEBUG, + "\n ADC Decoder interface interrupt \r\n"); + } + if ((Intrsts & XRFDC_ADC_IXR_DATAPATH_MASK) != 0U) { + IntrMask |= Intrsts & XRFDC_ADC_IXR_DATAPATH_MASK; + metal_log(METAL_LOG_DEBUG, + "\n ADC Data Path interface interrupt \r\n"); + } + } else { + /* DAC */ + if ((Intrsts & XRFDC_IXR_FIFOUSRDAT_MASK) != 0U) { + IntrMask |= Intrsts & XRFDC_IXR_FIFOUSRDAT_MASK; + metal_log(METAL_LOG_DEBUG, "\n DAC FIFO interface interrupt \r\n"); + } + if ((Intrsts & XRFDC_DAC_IXR_DATAPATH_MASK) != 0U) { + IntrMask |= Intrsts & XRFDC_DAC_IXR_DATAPATH_MASK; + metal_log(METAL_LOG_DEBUG, "\n DAC Data Path interface interrupt \r\n"); + } + } + Block = Block_Id; + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + if ((Block_Id == XRFDC_BLK_ID0) || (Block_Id == XRFDC_BLK_ID1)) { + Block = XRFDC_BLK_ID0; + } else { + Block = XRFDC_BLK_ID1; + } + } + InstancePtr->StatusHandler(InstancePtr->CallBackRef, Type, Tile_Id, + Block, IntrMask); + + /* Clear the interrupt */ + if (Type == XRFDC_ADC_TILE) { + /* ADC */ + XRFdc_IntrClr(InstancePtr, XRFDC_ADC_TILE, Tile_Id, Block_Id, Intrsts); + + } else { + /* DAC */ + XRFdc_IntrClr(InstancePtr, XRFDC_DAC_TILE, Tile_Id, Block_Id, Intrsts); + } + + return (u32)METAL_IRQ_HANDLED; +} + +/*****************************************************************************/ +/** +* +* This function sets the status callback function, the status handler, which the +* driver calls when it encounters conditions that should be reported to the +* higher layer software. The handler executes in an interrupt context, so +* the amount of processing should be minimized +* +* +* @param InstancePtr is a pointer to the XRFdc instance. +* @param CallBackRef is the upper layer callback reference passed back +* when the callback function is invoked. +* @param FunctionPtr is the pointer to the callback function. +* +* @return None. +* +* @note +* +* The handler is called within interrupt context, so it should finish its +* work quickly. +* +******************************************************************************/ +void XRFdc_SetStatusHandler(XRFdc *InstancePtr, void *CallBackRef, + XRFdc_StatusHandler FunctionPtr) +{ + Xil_AssertVoid(InstancePtr != NULL); + Xil_AssertVoid(FunctionPtr != NULL); + Xil_AssertVoid(InstancePtr->IsReady == (u32)XRFDC_COMPONENT_IS_READY); + + InstancePtr->StatusHandler = FunctionPtr; + InstancePtr->CallBackRef = CallBackRef; +} + +/** @} */ diff --git a/mpm/lib/rfdc/xrfdc_mb.c b/mpm/lib/rfdc/xrfdc_mb.c new file mode 100644 index 000000000..c7b563d40 --- /dev/null +++ b/mpm/lib/rfdc/xrfdc_mb.c @@ -0,0 +1,786 @@ +/****************************************************************************** +* +* Copyright (C) 2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc_mb.c +* @addtogroup xrfdc_v6_0 +* @{ +* +* Contains the interface functions of the Mixer Settings in XRFdc driver. +* See xrfdc.h for a detailed description of the device and driver. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 6.0 cog 02/17/18 Initial release/handle alternate bound out. +* +* </pre> +* +******************************************************************************/ + +/***************************** Include Files *********************************/ +#include "mpm/rfdc/xrfdc.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static void XRFdc_SetSignalFlow(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Mode, u32 DigitalDataPathId, u32 MixerInOutDataType, + int ConnectIData, int ConnectQData); +static void XRFdc_MB_R2C_C2R(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 NoOfDataPaths, u32 MixerInOutDataType, u32 Mode, + u32 DataPathIndex[], u32 BlockIndex[]); +static void XRFdc_MB_C2C(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 NoOfDataPaths, u32 MixerInOutDataType, u32 Mode, + u32 DataPathIndex[], u32 BlockIndex[]); +static void XRFdc_SB_R2C_C2R(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 MixerInOutDataType, u32 Mode, u32 DataPathIndex[], u32 BlockIndex[]); +static void XRFdc_SB_C2C(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 MixerInOutDataType, u32 Mode, u32 DataPathIndex[], u32 BlockIndex[]); +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** +* +* Static API to setup Singleband configuration for C2C MixerInOutDataType +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param MixerInOutDataType is mixer data type, valid values are XRFDC_MB_DATATYPE_* +* @param Mode is connection mode SB/MB_2X/MB_4X. +* @param DataPathIndex is the array that represents the blocks enabled in +* DigitalData Path. +* @param BlockIndex is the array that represents the blocks enabled in +* Analog Path(Data Converters). +* +* @return +* - None +* +* @note Static API for ADC/DAC blocks +* +******************************************************************************/ +static void XRFdc_SB_C2C(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 MixerInOutDataType, u32 Mode, u32 DataPathIndex[], u32 BlockIndex[]) +{ + u32 Block_Id; + + if ((Type == XRFDC_ADC_TILE) && (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1)) { + /* Update ConnectedIData and ConnectedQData for ADC 4GSPS */ + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = BlockIndex[1U]; + Block_Id = (DataPathIndex[0] == 0U ? 1U : 0U); + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Block_Id]. + ConnectedIData = -1; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[Block_Id]. + ConnectedQData = -1; + + if (DataPathIndex[0] == XRFDC_BLK_ID1) { + DataPathIndex[0] = XRFDC_BLK_ID2; + } + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, BlockIndex[0U], BlockIndex[0U]+1U); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0]+1U, + MixerInOutDataType, BlockIndex[1U]+1U, BlockIndex[1U]+2U); + Block_Id = (DataPathIndex[0] == XRFDC_BLK_ID2 ? XRFDC_BLK_ID0 : + XRFDC_BLK_ID2); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, Block_Id, + MixerInOutDataType, -1, -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, Block_Id+1U, + MixerInOutDataType, -1, -1); + } else { + DataPathIndex[1] = BlockIndex[0] + BlockIndex[1] - DataPathIndex[0]; + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, BlockIndex[0], BlockIndex[1]); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[1], + MixerInOutDataType, -1, -1); + + /* Update ConnectedIData and ConnectedQData for DAC and ADC 2GSPS */ + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = BlockIndex[1]; + + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = -1; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = -1; + } else { + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = BlockIndex[1]; + + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = -1; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = -1; + } + } +} + +/*****************************************************************************/ +/** +* +* Static API to setup Singleband configuration for C2R and R2C MultiBandDataTypes +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param MixerInOutDataType is mixer data type, valid values are XRFDC_MB_DATATYPE_* +* @param Mode is connection mode SB/MB_2X/MB_4X. +* @param DataPathIndex is the array that represents the blocks enabled in +* DigitalData Path. +* @param BlockIndex is the array that represents the blocks enabled in +* Analog Path(Data Converters). +* +* @return +* - None +* +* @note Static API for ADC/DAC blocks +* +******************************************************************************/ +static void XRFdc_SB_R2C_C2R(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 MixerInOutDataType, u32 Mode, u32 DataPathIndex[], u32 BlockIndex[]) +{ + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = -1; + } else { + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = -1; + } + if ((Type == XRFDC_ADC_TILE) && (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1)) { + if (DataPathIndex[0] == XRFDC_BLK_ID1) { + DataPathIndex[0] = XRFDC_BLK_ID2; + } + if (BlockIndex[0] == XRFDC_BLK_ID1) { + BlockIndex[0] = XRFDC_BLK_ID2; + } + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0]+1U, + MixerInOutDataType, BlockIndex[0U]+1U, -1); + } + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, BlockIndex[0U], -1); +} + +/*****************************************************************************/ +/** +* +* Static API to setup Multiband configuration for C2C MixerInOutDataType +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param MixerInOutDataType is mixer data type, valid values are XRFDC_MB_DATATYPE_* +* @param Mode is connection mode SB/MB_2X/MB_4X. +* @param DataPathIndex is the array that represents the blocks enabled in +* DigitalData Path. +* @param BlockIndex is the array that represents the blocks enabled in +* Analog Path(Data Converters). +* +* @return +* - None +* +* @note Static API for ADC/DAC blocks +* +******************************************************************************/ +static void XRFdc_MB_C2C(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 NoOfDataPaths, u32 MixerInOutDataType, u32 Mode, + u32 DataPathIndex[], u32 BlockIndex[]) +{ + if ((Type == XRFDC_ADC_TILE) && (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1)) { + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, BlockIndex[0U], BlockIndex[0U]+1U); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0]+1U, + MixerInOutDataType, BlockIndex[0U]+2U, BlockIndex[0U]+3U); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0]+2U, + MixerInOutDataType, BlockIndex[0U], BlockIndex[0U]+1U); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0]+3U, + MixerInOutDataType, BlockIndex[0U]+2U, BlockIndex[0U]+3U); + + /* Update ConnectedIData and ConnectedQData for ADC 4GSPS */ + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = BlockIndex[1U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = BlockIndex[1U]; + } else if (NoOfDataPaths == 2U) { + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, BlockIndex[0U], BlockIndex[1U]); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[1], + MixerInOutDataType, BlockIndex[0U], BlockIndex[1U]); + + /* Update ConnectedIData and ConnectedQData for DAC and ADC 2GSPS */ + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = BlockIndex[1U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = BlockIndex[1U]; + } else { + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = BlockIndex[1U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = BlockIndex[1U]; + } + } + if (NoOfDataPaths == 4U) { + if (Type == XRFDC_ADC_TILE) { + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, BlockIndex[0U], BlockIndex[1U]); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[1], + MixerInOutDataType, BlockIndex[0U], BlockIndex[1U]); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[2], + MixerInOutDataType, BlockIndex[0U], BlockIndex[1U]); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[3], + MixerInOutDataType, BlockIndex[0U], BlockIndex[1U]); + + /* Update ConnectedIData and ConnectedQData for ADC 4GSPS */ + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = BlockIndex[1U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = BlockIndex[1U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[2]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[2]]. + ConnectedQData = BlockIndex[1U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[3]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[3]]. + ConnectedQData = BlockIndex[1U]; + } else { + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, DataPathIndex[0], DataPathIndex[1U]); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[1], + MixerInOutDataType, DataPathIndex[0U], DataPathIndex[1U]); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[2], + MixerInOutDataType, DataPathIndex[2U], DataPathIndex[3U]); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[3], + MixerInOutDataType, DataPathIndex[2U], DataPathIndex[3U]); + + /* Update ConnectedIData and ConnectedQData for DAC and ADC 2GSPS */ + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = BlockIndex[1U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = BlockIndex[1U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[2]]. + ConnectedIData = DataPathIndex[0U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[2]]. + ConnectedQData = DataPathIndex[1U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[3]]. + ConnectedIData = DataPathIndex[0U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[3]]. + ConnectedQData = DataPathIndex[1U]; + } + } +} + +/*****************************************************************************/ +/** +* +* Static API to setup Multiband configuration for C2C MixerInOutDataType +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param MixerInOutDataType is mixer data type, valid values are XRFDC_MB_DATATYPE_* +* @param Mode is connection mode SB/MB_2X/MB_4X. +* @param DataPathIndex is the array that represents the blocks enabled in +* DigitalData Path. +* @param BlockIndex is the array that represents the blocks enabled in +* Analog Path(Data Converters). +* +* @return +* - None +* +* @note Static API for ADC/DAC blocks +* +******************************************************************************/ +static void XRFdc_MB_R2C_C2R(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 NoOfDataPaths, u32 MixerInOutDataType, u32 Mode, + u32 DataPathIndex[], u32 BlockIndex[]) +{ + if ((Type == XRFDC_ADC_TILE) && (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1)) { + /* Update ConnectedIData and ConnectedQData */ + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = -1; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = -1; + if (BlockIndex[0] == XRFDC_BLK_ID1) { + BlockIndex[0] = XRFDC_BLK_ID2; + } + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, BlockIndex[0U], -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[1], + MixerInOutDataType, BlockIndex[0U]+1U, -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0]+2U, + MixerInOutDataType, BlockIndex[0U], -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[1]+2U, + MixerInOutDataType, BlockIndex[0U]+1U, -1); + } else if (NoOfDataPaths == 2U) { + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, BlockIndex[0], -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[1], + MixerInOutDataType, BlockIndex[0], -1); + + /* Update ConnectedIData and ConnectedQData */ + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = -1; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = -1; + } else { + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = -1; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = -1; + } + + } + if (NoOfDataPaths == 4U) { + if (Type == XRFDC_ADC_TILE) { + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, BlockIndex[0], -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[1], + MixerInOutDataType, BlockIndex[0], -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[2], + MixerInOutDataType, BlockIndex[0], -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[3], + MixerInOutDataType, BlockIndex[0], -1); + + /* Update ConnectedIData and ConnectedQData */ + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = -1; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = -1; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[2]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[2]]. + ConnectedQData = -1; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[3]]. + ConnectedIData = BlockIndex[0U]; + InstancePtr->ADC_Tile[Tile_Id].ADCBlock_Digital_Datapath[DataPathIndex[3]]. + ConnectedQData = -1; + + } else { + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[0], + MixerInOutDataType, DataPathIndex[0], -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[1], + MixerInOutDataType, DataPathIndex[0], -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[2], + MixerInOutDataType, DataPathIndex[2], -1); + XRFdc_SetSignalFlow(InstancePtr, Type, Tile_Id, Mode, DataPathIndex[3], + MixerInOutDataType, DataPathIndex[2], -1); + + /* Update ConnectedIData and ConnectedQData */ + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedIData = DataPathIndex[0]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[0]]. + ConnectedQData = -1; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedIData = DataPathIndex[0]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[1]]. + ConnectedQData = -1; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[2]]. + ConnectedIData = DataPathIndex[0]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[2]]. + ConnectedQData = -1; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[3]]. + ConnectedIData = DataPathIndex[0]; + InstancePtr->DAC_Tile[Tile_Id].DACBlock_Digital_Datapath[DataPathIndex[3]]. + ConnectedQData = -1; + } + } +} + +/*****************************************************************************/ +/** +* +* Static API to update mode and MultibandConfig +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param NoOfDataPaths is number of DataPaths enabled. +* @param ModePtr is a pointer to connection mode SB/MB_2X/MB_4X. +* @param DataPathIndex is the array that represents the blocks enabled in +* DigitalData Path. +* +* @return +* - None +* +* @note Static API for ADC/DAC blocks +* +******************************************************************************/ +static u32 XRFdc_UpdateMBConfig(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 NoOfDataPaths, u32 *ModePtr, u32 DataPathIndex[]) +{ + u8 MultibandConfig; + u32 Status; + + if (Type == XRFDC_ADC_TILE) { + MultibandConfig = InstancePtr->ADC_Tile[Tile_Id].MultibandConfig; + } else { + MultibandConfig = InstancePtr->DAC_Tile[Tile_Id].MultibandConfig; + } + + if (NoOfDataPaths == 1U) { + *ModePtr = XRFDC_SINGLEBAND_MODE; + if (((DataPathIndex[0] == XRFDC_BLK_ID2) || + (DataPathIndex[0] == XRFDC_BLK_ID3)) && + ((MultibandConfig == XRFDC_MB_MODE_2X_BLK01_BLK23) || + (MultibandConfig == XRFDC_MB_MODE_4X))) { + MultibandConfig = XRFDC_MB_MODE_2X_BLK01; + } else if (((DataPathIndex[0] == XRFDC_BLK_ID0) || + (DataPathIndex[0] == XRFDC_BLK_ID1)) && + ((MultibandConfig == XRFDC_MB_MODE_2X_BLK01_BLK23) || + (MultibandConfig == XRFDC_MB_MODE_4X))) { + MultibandConfig = XRFDC_MB_MODE_2X_BLK23; + } else if ((MultibandConfig == XRFDC_MB_MODE_2X_BLK01) && + ((DataPathIndex[0] == XRFDC_BLK_ID0) || + (DataPathIndex[0] == XRFDC_BLK_ID1))) { + MultibandConfig = XRFDC_MB_MODE_SB; + } else if ((MultibandConfig == XRFDC_MB_MODE_2X_BLK23) && + ((DataPathIndex[0] == XRFDC_BLK_ID2) || + (DataPathIndex[0] == XRFDC_BLK_ID3))) { + MultibandConfig = XRFDC_MB_MODE_SB; + } + } else if (NoOfDataPaths == 2U) { + *ModePtr = XRFDC_MULTIBAND_MODE_2X; + if (((MultibandConfig == XRFDC_MB_MODE_2X_BLK01) && + (DataPathIndex[0] == XRFDC_BLK_ID2) && (DataPathIndex[1] == XRFDC_BLK_ID3)) || + ((MultibandConfig == XRFDC_MB_MODE_2X_BLK23) && (DataPathIndex[0] == XRFDC_BLK_ID0) && + (DataPathIndex[1] == XRFDC_BLK_ID1)) || (MultibandConfig == XRFDC_MB_MODE_4X)) { + MultibandConfig = XRFDC_MB_MODE_2X_BLK01_BLK23; + } else if (((DataPathIndex[0] == XRFDC_BLK_ID2) && (DataPathIndex[1] == XRFDC_BLK_ID3)) && + (MultibandConfig == XRFDC_MB_MODE_SB)) { + MultibandConfig = XRFDC_MB_MODE_2X_BLK23; + } else if (((DataPathIndex[0] == XRFDC_BLK_ID0) && (DataPathIndex[1] == XRFDC_BLK_ID1)) && + (MultibandConfig == XRFDC_MB_MODE_SB)) { + MultibandConfig = XRFDC_MB_MODE_2X_BLK01; + } + } else if (NoOfDataPaths == 4U) { + *ModePtr = XRFDC_MULTIBAND_MODE_4X; + MultibandConfig = XRFDC_MB_MODE_4X; + } else { + metal_log(METAL_LOG_ERROR, "\n Invalid DigitalDataPathMask " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + /* Update Multiband Config member */ + if (Type == XRFDC_ADC_TILE) { + InstancePtr->ADC_Tile[Tile_Id].MultibandConfig = MultibandConfig; + } else { + InstancePtr->DAC_Tile[Tile_Id].MultibandConfig = MultibandConfig; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* User-level API to setup multiband configuration. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param DigitalDataPathMask is the DataPath mask. First 4 bits represent +* 4 data paths, 1 means enabled and 0 means disabled. +* @param MixerInOutDataType is mixer data type, valid values are XRFDC_MB_DATATYPE_* +* @param DataConverterMask is block enabled mask (input/output driving +* blocks). 1 means enabled and 0 means disabled. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note Common API for ADC/DAC blocks +* +******************************************************************************/ +u32 XRFdc_MultiBand(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u8 DigitalDataPathMask, u32 MixerInOutDataType, u32 DataConverterMask) +{ + u32 Status; + u32 Block_Id; + u8 NoOfDataPaths = 0U; + u32 BlockIndex[XRFDC_NUM_OF_BLKS4] = {XRFDC_BLK_ID4}; + u32 DataPathIndex[XRFDC_NUM_OF_BLKS4] = {XRFDC_BLK_ID4}; + u32 NoOfDataConverters = 0U; + u32 Mode = 0x0; + u32 NoOfBlocks = XRFDC_BLK_ID4; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + if ((DigitalDataPathMask == 0U) || (DigitalDataPathMask > 0xFU)) { + metal_log(METAL_LOG_ERROR, "\n Invalid DigitalDataPathMask " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + if ((DataConverterMask == 0U) || (DataConverterMask > 0xFU)) { + metal_log(METAL_LOG_ERROR, "\n Invalid DataConverterMask " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + if ((MixerInOutDataType != XRFDC_MB_DATATYPE_C2C) && + (MixerInOutDataType != XRFDC_MB_DATATYPE_R2C) && + (MixerInOutDataType != XRFDC_MB_DATATYPE_C2R)) { + metal_log(METAL_LOG_ERROR, "\n Invalid MixerInOutDataType " + "value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + NoOfBlocks = XRFDC_BLK_ID2; + } + /* Identify DataPathIndex and BlockIndex */ + for (Block_Id = XRFDC_BLK_ID0; Block_Id < NoOfBlocks; Block_Id++) { + if ((DataConverterMask & (1U << Block_Id)) != 0U) { + BlockIndex[NoOfDataConverters] = Block_Id; + NoOfDataConverters += 1U; + Status = XRFdc_CheckBlockEnabled(InstancePtr, Type, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block not " + "available in %s\r\n", __func__); + goto RETURN_PATH; + } + } + if ((DigitalDataPathMask & (1U << Block_Id)) != 0U) { + DataPathIndex[NoOfDataPaths] = Block_Id; + NoOfDataPaths += 1U; + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, Type, Tile_Id, + Block_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Requested block digital path " + "not enabled in %s\r\n", __func__); + goto RETURN_PATH; + } + } + } + + /* rerouting & configuration for alternative bonding. */ + if ((Type == XRFDC_DAC_TILE) && (DataConverterMask & 0x05) && (MixerInOutDataType == XRFDC_MB_DATATYPE_C2C) && + (InstancePtr->RFdc_Config.DACTile_Config[Tile_Id].NumSlices == 2)) { + BlockIndex[XRFDC_BLK_ID1] = XRFDC_BLK_ID1; + XRFdc_ClrSetReg(InstancePtr, XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, XRFDC_BLK_ID1), + XRFDC_DAC_MB_CFG_OFFSET, XRFDC_ALT_BOND_MASK, XRFDC_ENABLED << XRFDC_ALT_BOND_SHIFT); + XRFdc_ClrSetReg(InstancePtr, XRFDC_BLOCK_BASE(XRFDC_DAC_TILE, Tile_Id, XRFDC_BLK_ID2), + XRFDC_DAC_MB_CFG_OFFSET, XRFDC_ALT_BOND_MASK, XRFDC_ENABLED << XRFDC_ALT_BOND_SHIFT); + } + + if (BlockIndex[0] != DataPathIndex[0]) { + metal_log(METAL_LOG_ERROR, "\n Not a valid MB/SB " + "combination in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + /* UPdate MultibandConfig in driver instance */ + Status = XRFdc_UpdateMBConfig(InstancePtr, Type, Tile_Id, NoOfDataPaths, &Mode, + DataPathIndex); + if (Status != XRFDC_SUCCESS) { + goto RETURN_PATH; + } + + if ((MixerInOutDataType == XRFDC_MB_DATATYPE_C2C) && (Mode == XRFDC_SINGLEBAND_MODE)) { + /* Singleband C2C */ + XRFdc_SB_C2C(InstancePtr, Type, Tile_Id, MixerInOutDataType, Mode, + DataPathIndex, BlockIndex); + } else if (((MixerInOutDataType == XRFDC_MB_DATATYPE_R2C) || + (MixerInOutDataType == XRFDC_MB_DATATYPE_C2R)) && (Mode == XRFDC_SINGLEBAND_MODE)) { + /* Singleband R2C and C2R */ + XRFdc_SB_R2C_C2R(InstancePtr, Type, Tile_Id, MixerInOutDataType, Mode, + DataPathIndex, BlockIndex); + } + if ((MixerInOutDataType == XRFDC_MB_DATATYPE_C2C) && + ((Mode == XRFDC_MULTIBAND_MODE_2X) || (Mode == XRFDC_MULTIBAND_MODE_4X))) { + /* Multiband C2C */ + XRFdc_MB_C2C(InstancePtr, Type, Tile_Id, NoOfDataPaths, MixerInOutDataType, Mode, + DataPathIndex, BlockIndex); + } else if (((MixerInOutDataType == XRFDC_MB_DATATYPE_R2C) || (MixerInOutDataType == XRFDC_MB_DATATYPE_C2R)) && + ((Mode == XRFDC_MULTIBAND_MODE_2X) || (Mode == XRFDC_MULTIBAND_MODE_4X))) { + /* Multiband C2R and R2C */ + XRFdc_MB_R2C_C2R(InstancePtr, Type, Tile_Id, NoOfDataPaths, MixerInOutDataType, + Mode, DataPathIndex, BlockIndex); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/*****************************************************************************/ +/** +* +* Sets up signal flow configuration. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Mode is connection mode SB/MB_2X/MB_4X. +* @param DigitalDataPathId for the requested I or Q data. +* @param MixerInOutDataType is mixer data type, valid values are XRFDC_MB_DATATYPE_* +* @param ConnectIData is analog blocks that are connected to +* DigitalDataPath I. +* @param ConnectQData is analog blocks that are connected to +* DigitalDataPath Q. +* +* @return None +* +* @note static API used internally. +* +******************************************************************************/ +static void XRFdc_SetSignalFlow(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Mode, u32 DigitalDataPathId, u32 MixerInOutDataType, + int ConnectIData, int ConnectQData) +{ + u16 ReadReg; + u32 BaseAddr; + + Xil_AssertVoid(InstancePtr != NULL); + Xil_AssertVoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, DigitalDataPathId); + if (Type == XRFDC_ADC_TILE) { + /* ADC */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_SWITCH_MATRX_OFFSET); + ReadReg &= ~XRFDC_SWITCH_MTRX_MASK; + if (ConnectIData != -1) { + ReadReg |= ((u16)ConnectIData) << XRFDC_SEL_CB_TO_MIX0_SHIFT; + } + if (ConnectQData != -1) { + ReadReg |= (u16)ConnectQData; + } + if ((MixerInOutDataType == XRFDC_MB_DATATYPE_C2C) && + (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1)) { + ReadReg |= XRFDC_SEL_CB_TO_QMC_MASK; + } + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + ReadReg |= XRFDC_SEL_CB_TO_DECI_MASK; + } + + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_SWITCH_MATRX_OFFSET, ReadReg); + } else { + /* DAC */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_DAC_MB_CFG_OFFSET); + ReadReg &= ~XRFDC_MB_CFG_MASK; + if (Mode == XRFDC_SINGLEBAND_MODE) { + if ((u32)ConnectIData == DigitalDataPathId) { + if (ConnectQData != -1) { + ReadReg |= XRFDC_SB_C2C_BLK0; + } else { + ReadReg |= XRFDC_SB_C2R; + } + } + if ((ConnectIData == -1) && (ConnectQData == -1)) { + ReadReg |= XRFDC_SB_C2C_BLK1; + } + } else { + if (Mode == XRFDC_MULTIBAND_MODE_4X) { + ReadReg |= XRFDC_MB_EN_4X_MASK; + } + if ((u32)ConnectIData == DigitalDataPathId) { + if (ConnectQData != -1) { + ReadReg |= XRFDC_MB_C2C_BLK0; + } else { + ReadReg |= XRFDC_MB_C2R_BLK0; + } + } else { + if (ConnectQData != -1) { + ReadReg |= XRFDC_MB_C2C_BLK1; + } else { + ReadReg |= XRFDC_MB_C2R_BLK1; + } + } + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_DAC_MB_CFG_OFFSET, ReadReg); + } +} + +/** @} */
\ No newline at end of file diff --git a/mpm/lib/rfdc/xrfdc_mixer.c b/mpm/lib/rfdc/xrfdc_mixer.c new file mode 100644 index 000000000..747e180a4 --- /dev/null +++ b/mpm/lib/rfdc/xrfdc_mixer.c @@ -0,0 +1,1091 @@ +/****************************************************************************** +* +* Copyright (C) 2018-2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc_mixer.c +* @addtogroup xrfdc_v6_0 +* @{ +* +* Contains the interface functions of the Mixer Settings in XRFdc driver. +* See xrfdc.h for a detailed description of the device and driver. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 5.0 sk 08/06/18 Initial release +* 5.1 cog 01/29/19 Replace structure reference ADC checks with +* function. +* cog 01/29/19 XRFdc_SetCoarseMixer and MixerRangeCheck now need +* Tile_id as a parameter. +* cog 01/29/19 Rename DataType to MixerInputDataType for +* readability. +* </pre> +* +******************************************************************************/ + +/***************************** Include Files *********************************/ +#include "mpm/rfdc/xrfdc.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static void XRFdc_SetFineMixer(XRFdc *InstancePtr, u32 BaseAddr, + XRFdc_Mixer_Settings *MixerSettingsPtr); +static void XRFdc_SetCoarseMixer(XRFdc *InstancePtr, u32 Type, u32 BaseAddr, + u32 Tile_Id, u32 Block_Id, u32 CoarseMixFreq, XRFdc_Mixer_Settings *MixerSettingsPtr); +static u32 XRFdc_MixerRangeCheck(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + XRFdc_Mixer_Settings *MixerSettingsPtr); + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** +* The API is used to update various mixer settings, fine, coarse, NCO etc. +* Mixer/NCO settings passed are used to update the corresponding +* block level registers. Driver structure is updated with the new values. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param MixerSettingsPtr Pointer to the XRFdc_Mixer_Settings structure +* in which the Mixer/NCO settings are passed. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note FineMixerScale in Mixer_Settings structure can have 3 values. +* XRFDC_MIXER_SCALE_* represents the valid values. +* XRFDC_MIXER_SCALE_AUTO - If mixer mode is R2C, Mixer Scale is +* set to 1 and for other modes mixer scale is set to 0.7 +* XRFDC_MIXER_SCALE_1P0 - To set fine mixer scale to 1. +* XRFDC_MIXER_SCALE_0P7 - To set fine mixer scale to 0.7. +* +******************************************************************************/ +u32 XRFdc_SetMixerSettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_Mixer_Settings *MixerSettingsPtr) +{ + u32 Status; + u16 ReadReg; + u32 BaseAddr; + double SamplingRate; + s64 Freq; + s32 PhaseOffset; + u32 NoOfBlocks; + u32 Index; + XRFdc_Mixer_Settings *MixerConfigPtr; + u8 CalibrationMode = 0U; + u32 CoarseMixFreq; + double NCOFreq; + u32 NyquistZone = 0U; + u32 Offset; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(MixerSettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + goto RETURN_PATH; + } + + Status = XRFdc_MixerRangeCheck(InstancePtr, Type, Tile_Id, MixerSettingsPtr); + if (Status != XRFDC_SUCCESS) { + goto RETURN_PATH; + } + + Index = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + NoOfBlocks = XRFDC_NUM_OF_BLKS2; + if (Block_Id == XRFDC_BLK_ID1) { + Index = XRFDC_BLK_ID2; + NoOfBlocks = XRFDC_NUM_OF_BLKS4; + } + } else { + NoOfBlocks = Block_Id + 1U; + } + + for (; Index < NoOfBlocks; Index++) { + if (Type == XRFDC_ADC_TILE) { + /* ADC */ + MixerConfigPtr = &InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index].Mixer_Settings; + SamplingRate = InstancePtr->ADC_Tile[Tile_Id]. + PLL_Settings.SampleRate; + } else { + /* DAC */ + MixerConfigPtr = &InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Index].Mixer_Settings; + SamplingRate = InstancePtr->DAC_Tile[Tile_Id]. + PLL_Settings.SampleRate; + } + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Index); + + if (SamplingRate <= 0) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Incorrect Sampling " + "rate in %s\r\n", __func__); + goto RETURN_PATH; + } else { + metal_log(METAL_LOG_DEBUG, "\n Sampling " + "rate is %2.4f in %s\r\n", SamplingRate, __func__); + } + + SamplingRate *= XRFDC_MILLI; + /* Set MixerInputDataType for ADC and DAC */ + if (Type == XRFDC_DAC_TILE) { + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_DAC_ITERP_DATA_OFFSET); + ReadReg &= ~XRFDC_DAC_INTERP_DATA_MASK; + InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Index].MixerInputDataType = + XRFDC_DATA_TYPE_REAL; + if ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) || + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2R)) { + ReadReg |= XRFDC_DAC_INTERP_DATA_MASK; + InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Index]. + MixerInputDataType = XRFDC_DATA_TYPE_IQ; + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_DAC_ITERP_DATA_OFFSET, ReadReg); + } else { + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_DECI_CONFIG_OFFSET); + ReadReg &= ~XRFDC_DEC_CFG_MASK; + if (XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) { + ReadReg |= XRFDC_DEC_CFG_4GSPS_MASK; + } else if ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) || + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_R2C)) { + ReadReg |= XRFDC_DEC_CFG_IQ_MASK; + } else { + ReadReg |= XRFDC_DEC_CFG_CHA_MASK; + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_DECI_CONFIG_OFFSET, ReadReg); + if (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) { + InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index]. + MixerInputDataType = XRFDC_DATA_TYPE_IQ; + } + if ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_R2C) || + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_R2R)) { + InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Index]. + MixerInputDataType = XRFDC_DATA_TYPE_REAL; + } + } + + /* Set NCO Phase Mode */ + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + if ((Index == XRFDC_BLK_ID0) || (Index == XRFDC_BLK_ID2)) { + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_NCO_PHASE_MOD_OFFSET, + XRFDC_NCO_PHASE_MOD_EVEN); + } else { + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_NCO_PHASE_MOD_OFFSET, + XRFDC_NCO_PHASE_MODE_ODD); + } + } + + /* Update NCO, CoarseMix freq based on calibration mode */ + CoarseMixFreq = MixerSettingsPtr->CoarseMixFreq; + NCOFreq = MixerSettingsPtr->Freq; + if (Type == XRFDC_ADC_TILE) { + Status = XRFdc_GetCalibrationMode(InstancePtr, + Tile_Id, Block_Id, &CalibrationMode); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + if (CalibrationMode == XRFDC_CALIB_MODE1) { + switch (CoarseMixFreq) { + case XRFDC_COARSE_MIX_BYPASS: + CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO; + break; + case XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR: + CoarseMixFreq = + XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR; + break; + case XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO: + CoarseMixFreq = + XRFDC_COARSE_MIX_BYPASS; + break; + case XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR: + CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR; + break; + default: + CoarseMixFreq = + XRFDC_COARSE_MIX_OFF; + break; + } + NCOFreq -= SamplingRate / 2.0; + } + } + + if ((NCOFreq < -(SamplingRate / 2.0)) || + (NCOFreq > (SamplingRate / 2.0))) { + Status = XRFdc_GetNyquistZone(InstancePtr, Type, + Tile_Id, Block_Id, &NyquistZone); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + do { + if (NCOFreq < -(SamplingRate / 2.0)) { + NCOFreq += SamplingRate; + } + if (NCOFreq > (SamplingRate / 2.0)) { + NCOFreq -= SamplingRate; + } + } while ((NCOFreq < -(SamplingRate / 2.0)) || + (NCOFreq > (SamplingRate / 2.0))); + + if ((NyquistZone == XRFDC_EVEN_NYQUIST_ZONE) && + (NCOFreq != 0)) { + NCOFreq *= -1; + } + } + + /* NCO Frequency */ + if (NCOFreq < 0) { + Freq = ((NCOFreq * XRFDC_NCO_FREQ_MIN_MULTIPLIER) / + SamplingRate); + } else { + Freq = ((NCOFreq * XRFDC_NCO_FREQ_MULTIPLIER) / + SamplingRate); + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_NCO_FQWD_LOW_OFFSET, (u16)Freq); + ReadReg = (Freq >> XRFDC_NCO_FQWD_MID_SHIFT) & XRFDC_NCO_FQWD_MID_MASK; + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_NCO_FQWD_MID_OFFSET, (u16)ReadReg); + ReadReg = (Freq >> XRFDC_NCO_FQWD_UPP_SHIFT) & XRFDC_NCO_FQWD_UPP_MASK; + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_NCO_FQWD_UPP_OFFSET, (u16)ReadReg); + + /* Phase Offset */ + PhaseOffset = ((MixerSettingsPtr->PhaseOffset * + XRFDC_NCO_PHASE_MULTIPLIER) / XRFDC_MIXER_PHASE_OFFSET_UP_LIMIT); + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_NCO_PHASE_LOW_OFFSET, + (u16)PhaseOffset); + + ReadReg = (PhaseOffset >> XRFDC_NCO_PHASE_UPP_SHIFT) & + XRFDC_NCO_PHASE_UPP_MASK; + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_NCO_PHASE_UPP_OFFSET, + ReadReg); + + if (MixerSettingsPtr->MixerType == XRFDC_MIXER_TYPE_COARSE) { + XRFdc_SetCoarseMixer(InstancePtr, Type, BaseAddr, Tile_Id, + Index, CoarseMixFreq, MixerSettingsPtr); + } else { + XRFdc_SetFineMixer(InstancePtr, BaseAddr, MixerSettingsPtr); + } + + /* Fine Mixer Scale */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_MXR_MODE_OFFSET); + if (MixerSettingsPtr->FineMixerScale == XRFDC_MIXER_SCALE_1P0) { + ReadReg |= XRFDC_FINE_MIX_SCALE_MASK; + InstancePtr->UpdateMixerScale = 0x1U; + } else if (MixerSettingsPtr->FineMixerScale == XRFDC_MIXER_SCALE_0P7) { + ReadReg &= ~XRFDC_FINE_MIX_SCALE_MASK; + InstancePtr->UpdateMixerScale = 0x1U; + } else { + InstancePtr->UpdateMixerScale = 0x0U; + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_MXR_MODE_OFFSET, + ReadReg); + + /* Event Source */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_NCO_UPDT_OFFSET, + XRFDC_NCO_UPDT_MODE_MASK, MixerSettingsPtr->EventSource); + if (MixerSettingsPtr->EventSource == XRFDC_EVNT_SRC_IMMEDIATE) { + if (Type == XRFDC_ADC_TILE) { + Offset = XRFDC_ADC_UPDATE_DYN_OFFSET; + } else { + Offset = XRFDC_DAC_UPDATE_DYN_OFFSET; + } + XRFdc_ClrSetReg(InstancePtr, BaseAddr, Offset, + XRFDC_UPDT_EVNT_MASK, XRFDC_UPDT_EVNT_NCO_MASK); + } + + /* Update the instance with new values */ + MixerConfigPtr->EventSource = MixerSettingsPtr->EventSource; + MixerConfigPtr->PhaseOffset = MixerSettingsPtr->PhaseOffset; + MixerConfigPtr->MixerMode = MixerSettingsPtr->MixerMode; + MixerConfigPtr->CoarseMixFreq = MixerSettingsPtr->CoarseMixFreq; + MixerConfigPtr->Freq = MixerSettingsPtr->Freq; + MixerConfigPtr->MixerType = MixerSettingsPtr->MixerType; + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; + +} + +/*****************************************************************************/ +/** +* Static API used to do the Mixer Settings range check. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param MixerSettingsPtr Pointer to the XRFdc_Mixer_Settings structure +* in which the Mixer/NCO settings are passed. +* +* @return +* - XRFDC_SUCCESS if mixer settings are within the range. +* - XRFDC_FAILURE if mixer settings are not in valid range +* +* @note None +* +******************************************************************************/ +static u32 XRFdc_MixerRangeCheck(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + XRFdc_Mixer_Settings *MixerSettingsPtr) +{ + u32 Status; + + if ((MixerSettingsPtr->PhaseOffset >= + XRFDC_MIXER_PHASE_OFFSET_UP_LIMIT) || + (MixerSettingsPtr->PhaseOffset <= + XRFDC_MIXER_PHASE_OFFSET_LOW_LIMIT)) { + metal_log(METAL_LOG_ERROR, "\n Invalid phase offset value " + "in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((MixerSettingsPtr->EventSource > XRFDC_EVNT_SRC_PL) || + ((MixerSettingsPtr->EventSource == XRFDC_EVNT_SRC_MARKER) && + (Type == XRFDC_ADC_TILE))) { + metal_log(METAL_LOG_ERROR, "\n Invalid event source " + "selection in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (MixerSettingsPtr->MixerMode > XRFDC_MIXER_MODE_R2R) { + metal_log(METAL_LOG_ERROR, "\n Invalid fine mixer mode " + "in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if ((MixerSettingsPtr->CoarseMixFreq != XRFDC_COARSE_MIX_OFF) && + (MixerSettingsPtr->CoarseMixFreq != + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO) && + (MixerSettingsPtr->CoarseMixFreq != + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR) && + (MixerSettingsPtr->CoarseMixFreq != + XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR) && + (MixerSettingsPtr->CoarseMixFreq != + XRFDC_COARSE_MIX_BYPASS)) { + metal_log(METAL_LOG_ERROR, "\n Invalid coarse mix " + "frequency value in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + if (MixerSettingsPtr->FineMixerScale > XRFDC_MIXER_SCALE_0P7) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Mixer Scale in %s\r\n", __func__); + goto RETURN_PATH; + } + if (((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_R2C) && + (Type == XRFDC_DAC_TILE)) || + ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2R) && + (Type == XRFDC_ADC_TILE))) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Mixer mode in %s\r\n", __func__); + goto RETURN_PATH; + } + if ((MixerSettingsPtr->MixerType != XRFDC_MIXER_TYPE_FINE) && + (MixerSettingsPtr->MixerType != XRFDC_MIXER_TYPE_COARSE)) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, + "\n Invalid Mixer Type in %s\r\n", __func__); + goto RETURN_PATH; + } + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE) && + ((MixerSettingsPtr->EventSource == XRFDC_EVNT_SRC_SLICE) || + (MixerSettingsPtr->EventSource == + XRFDC_EVNT_SRC_IMMEDIATE))) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid Event Source, event " + "source is not supported in 4GSPS ADC %s\r\n", __func__); + goto RETURN_PATH; + } + if (((MixerSettingsPtr->MixerType == XRFDC_MIXER_TYPE_COARSE) && + (MixerSettingsPtr->CoarseMixFreq == XRFDC_COARSE_MIX_OFF)) || + ((MixerSettingsPtr->MixerType == XRFDC_MIXER_TYPE_FINE) && + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_OFF))) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid Combination of " + "Mixer settings in %s\r\n", __func__); + goto RETURN_PATH; + } + if ((MixerSettingsPtr->MixerType == XRFDC_MIXER_TYPE_COARSE) && + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_OFF)) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid Combination of " + "Mixer type and Mixer mode in %s\r\n", __func__); + goto RETURN_PATH; + } + if ((MixerSettingsPtr->MixerType == XRFDC_MIXER_TYPE_FINE) && + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_R2R)) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid Combination of " + "Mixer type and Mixer mode in %s\r\n", __func__); + goto RETURN_PATH; + } + if ((MixerSettingsPtr->MixerType == XRFDC_MIXER_TYPE_COARSE) && + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_R2R) && + (MixerSettingsPtr->CoarseMixFreq != XRFDC_COARSE_MIX_BYPASS)) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Invalid Combination of " + "Mixer type and Mixer mode in %s\r\n", __func__); + goto RETURN_PATH; + } + + Status = XRFDC_SUCCESS; + +RETURN_PATH: + return Status; +} + +/*****************************************************************************/ +/** +* Static API used to set the Fine Mixer. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param BaseAddr is ADC or DAC base address. +* @param CoarseMixFreq is ADC or DAC Coarse mixer frequency. +* @param MixerSettingsPtr Pointer to the XRFdc_Mixer_Settings structure +* in which the Mixer/NCO settings are passed. +* +* @return +* - None +* +* @note Static API +* +******************************************************************************/ +static void XRFdc_SetFineMixer(XRFdc *InstancePtr, u32 BaseAddr, + XRFdc_Mixer_Settings *MixerSettingsPtr) +{ + + if (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MXR_MODE_OFFSET, + (XRFDC_SEL_I_IQ_MASK | XRFDC_SEL_Q_IQ_MASK | + XRFDC_EN_I_IQ_MASK | XRFDC_EN_Q_IQ_MASK), + (XRFDC_EN_I_IQ_MASK | XRFDC_EN_Q_IQ_MASK | + XRFDC_I_IQ_COS_MINSIN | XRFDC_Q_IQ_SIN_COS)); + } else if (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2R) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MXR_MODE_OFFSET, + (XRFDC_EN_I_IQ_MASK | XRFDC_SEL_I_IQ_MASK | XRFDC_EN_Q_IQ_MASK), + (XRFDC_EN_I_IQ_MASK | XRFDC_I_IQ_COS_MINSIN)); + } else if (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_R2C) { + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MXR_MODE_OFFSET, + (XRFDC_EN_I_IQ_MASK | XRFDC_EN_Q_IQ_MASK | + XRFDC_SEL_I_IQ_MASK | XRFDC_SEL_Q_IQ_MASK | + XRFDC_FINE_MIX_SCALE_MASK), (XRFDC_EN_I_IQ | XRFDC_EN_Q_IQ | + XRFDC_I_IQ_COS_MINSIN | XRFDC_Q_IQ_SIN_COS | + XRFDC_FINE_MIX_SCALE_MASK)); + } else { + /* Fine mixer mode is OFF */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_MXR_MODE_OFFSET, + XRFDC_MIXER_MODE_OFF); + } + + /* Coarse Mixer is OFF */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_MXR_CFG0_OFFSET, + XRFDC_MIX_CFG0_MASK, XRFDC_CRSE_MIX_OFF); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_MXR_CFG1_OFFSET, + XRFDC_MIX_CFG1_MASK, XRFDC_CRSE_MIX_OFF); +} + +/*****************************************************************************/ +/** +* Static API used to set the Coarse Mixer. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param BaseAddr is ADC or DAC base address. +* @param Block_Id is ADC/DAC block number inside the tile. +* @param CoarseMixFreq is ADC or DAC Coarse mixer frequency. +* @param MixerSettingsPtr Pointer to the XRFdc_Mixer_Settings structure +* in which the Mixer/NCO settings are passed. +* +* @return +* - None +* +* @note Static API +* +******************************************************************************/ +static void XRFdc_SetCoarseMixer(XRFdc *InstancePtr, u32 Type, u32 BaseAddr, + u32 Tile_Id, u32 Block_Id, u32 CoarseMixFreq, XRFdc_Mixer_Settings *MixerSettingsPtr) +{ + u16 ReadReg; + + if (CoarseMixFreq == XRFDC_COARSE_MIX_BYPASS) { + /* Coarse Mix BYPASS */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_MXR_CFG0_OFFSET, + XRFDC_MIX_CFG0_MASK, XRFDC_CRSE_MIX_BYPASS); + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG1_OFFSET); + ReadReg &= ~XRFDC_MIX_CFG1_MASK; + if ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) || + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2R)) { + ReadReg |= XRFDC_CRSE_MIX_BYPASS; + } else { + ReadReg |= XRFDC_CRSE_MIX_OFF; + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG1_OFFSET, (u16)ReadReg); + } else if (CoarseMixFreq == XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO) { + /* Coarse Mix freq Fs/2 */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG0_OFFSET); + ReadReg &= ~XRFDC_MIX_CFG0_MASK; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_I_Q_FSBYTWO; + } else { + if ((Block_Id % 2U) == 0U) { + ReadReg |= XRFDC_CRSE_MIX_BYPASS; + } else { + ReadReg |= XRFDC_CRSE_4GSPS_ODD_FSBYTWO; + } + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG0_OFFSET, (u16)ReadReg); + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG1_OFFSET); + ReadReg &= ~XRFDC_MIX_CFG1_MASK; + if ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) || + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2R)) { + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_I_Q_FSBYTWO; + } else { + ReadReg |= ((Block_Id % 2U) == 0U) ? + XRFDC_CRSE_MIX_BYPASS : + XRFDC_CRSE_4GSPS_ODD_FSBYTWO; + } + } else { + ReadReg |= XRFDC_CRSE_MIX_OFF; + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG1_OFFSET, (u16)ReadReg); + } else if (CoarseMixFreq == XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR) { + /* Coarse Mix freq Fs/4 */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG0_OFFSET); + ReadReg &= ~XRFDC_MIX_CFG0_MASK; + if ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) || + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2R)) { + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_I_FSBYFOUR; + } else { + ReadReg |= ((Block_Id % 2U) == 0U) ? + XRFDC_CRSE_MIX_I_Q_FSBYTWO : + XRFDC_CRSE_MIX_I_ODD_FSBYFOUR; + } + } else { + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_R_I_FSBYFOUR; + } else { + ReadReg |= ((Block_Id % 2U) == 0U) ? + XRFDC_CRSE_MIX_I_Q_FSBYTWO : + XRFDC_CRSE_MIX_OFF; + } + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG0_OFFSET, (u16)ReadReg); + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG1_OFFSET); + ReadReg &= ~XRFDC_MIX_CFG1_MASK; + if ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) || + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2R)) { + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_Q_FSBYFOUR; + } else { + ReadReg |= ((Block_Id % 2U) == 0U) ? + XRFDC_CRSE_MIX_I_Q_FSBYTWO : + XRFDC_CRSE_MIX_Q_ODD_FSBYFOUR; + } + } else { + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_R_Q_FSBYFOUR; + } else { + ReadReg |= ((Block_Id % 2U) == 0U) ? + XRFDC_CRSE_MIX_OFF : + XRFDC_CRSE_MIX_Q_ODD_FSBYFOUR; + } + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG1_OFFSET, (u16)ReadReg); + } else if (CoarseMixFreq == XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR) { + /* Coarse Mix freq -Fs/4 */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG0_OFFSET); + ReadReg &= ~XRFDC_MIX_CFG0_MASK; + if ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) || + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2R)) { + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_I_MINFSBYFOUR; + } else { + ReadReg |= ((Block_Id % 2U) == 0U) ? + XRFDC_CRSE_MIX_I_Q_FSBYTWO : + XRFDC_CRSE_MIX_Q_ODD_FSBYFOUR; + } + } else { + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_R_I_MINFSBYFOUR; + } else { + ReadReg |= ((Block_Id % 2U) == 0U) ? + XRFDC_CRSE_MIX_I_Q_FSBYTWO : + XRFDC_CRSE_MIX_OFF; + } + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG0_OFFSET, (u16)ReadReg); + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG1_OFFSET); + ReadReg &= ~XRFDC_MIX_CFG1_MASK; + if ((MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2C) || + (MixerSettingsPtr->MixerMode == XRFDC_MIXER_MODE_C2R)) { + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_Q_MINFSBYFOUR; + } else { + ReadReg |= ((Block_Id % 2U) == 0U) ? + XRFDC_CRSE_MIX_I_Q_FSBYTWO : + XRFDC_CRSE_MIX_I_ODD_FSBYFOUR; + } + } else { + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + ReadReg |= XRFDC_CRSE_MIX_R_Q_MINFSBYFOUR; + } else { + ReadReg |= ((Block_Id % 2U) == 0U) ? + XRFDC_CRSE_MIX_OFF : + XRFDC_CRSE_MIX_I_ODD_FSBYFOUR; + } + } + XRFdc_WriteReg16(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG1_OFFSET, (u16)ReadReg); + } else if (CoarseMixFreq == XRFDC_COARSE_MIX_OFF) { + /* Coarse Mix OFF */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_MXR_CFG0_OFFSET, + XRFDC_MIX_CFG0_MASK, XRFDC_CRSE_MIX_OFF); + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_ADC_MXR_CFG1_OFFSET, + XRFDC_MIX_CFG1_MASK, XRFDC_CRSE_MIX_OFF); + } else { + metal_log(METAL_LOG_ERROR, "\n Invalid Coarse " + "Mixer frequency in %s\r\n", __func__); + } + + /* Fine mixer mode is OFF */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, XRFDC_MXR_MODE_OFFSET, + XRFDC_MIXER_MODE_OFF); +} + +/*****************************************************************************/ +/** +* +* The API returns back Mixer/NCO settings to the caller. +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Block_Id is ADC/DAC block number inside the tile. Valid values +* are 0-3. +* @param MixerSettingsPtr Pointer to the XRFdc_Mixer_Settings structure +* in which the Mixer/NCO settings are passed. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_FAILURE if Block not enabled. +* +* @note FineMixerScale in Mixer_Settings structure can have 3 values. +* XRFDC_MIXER_SCALE_* represents the valid values. +* XRFDC_MIXER_SCALE_AUTO - If mixer mode is R2C, Mixer Scale is +* set to 1 and for other modes mixer scale is set to 0.7 +* XRFDC_MIXER_SCALE_1P0 - To set fine mixer scale to 1. +* XRFDC_MIXER_SCALE_0P7 - To set fine mixer scale to 0.7. +* +******************************************************************************/ +u32 XRFdc_GetMixerSettings(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Block_Id, XRFdc_Mixer_Settings *MixerSettingsPtr) +{ + u32 Status; + u32 BaseAddr; + u64 ReadReg; + u64 ReadReg_Mix1; + double SamplingRate; + s64 Freq; + s32 PhaseOffset; + u32 Block; + u8 CalibrationMode = 0U; + XRFdc_Mixer_Settings *MixerConfigPtr; + u32 NyquistZone = 0U; + double NCOFreq; + u32 FineMixerMode; + u32 CoarseMixerMode = 0x0; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(MixerSettingsPtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckDigitalPathEnabled(InstancePtr, Type, Tile_Id, Block_Id); + if (Status != XRFDC_SUCCESS) { + goto RETURN_PATH; + } + + Block = Block_Id; + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 1) && + (Type == XRFDC_ADC_TILE)) { + if (Block_Id == XRFDC_BLK_ID1) { + Block_Id = XRFDC_BLK_ID3; + } + if (Block_Id == XRFDC_BLK_ID0) { + Block_Id = XRFDC_BLK_ID1; + } + } + + if (Type == XRFDC_ADC_TILE) { + /* ADC */ + SamplingRate = InstancePtr->ADC_Tile[Tile_Id].PLL_Settings. + SampleRate; + MixerConfigPtr = &InstancePtr->ADC_Tile[Tile_Id]. + ADCBlock_Digital_Datapath[Block_Id].Mixer_Settings; + } else { + /* DAC */ + SamplingRate = InstancePtr->DAC_Tile[Tile_Id].PLL_Settings. + SampleRate; + MixerConfigPtr = &InstancePtr->DAC_Tile[Tile_Id]. + DACBlock_Digital_Datapath[Block_Id].Mixer_Settings; + } + + if (SamplingRate <= 0) { + Status = XRFDC_FAILURE; + metal_log(METAL_LOG_ERROR, "\n Incorrect Sampling rate " + "in %s\r\n", __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_BLOCK_BASE(Type, Tile_Id, Block_Id); + SamplingRate *= XRFDC_MILLI; + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG0_OFFSET, XRFDC_MIX_CFG0_MASK); + ReadReg_Mix1 = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_ADC_MXR_CFG1_OFFSET, XRFDC_MIX_CFG1_MASK); + MixerSettingsPtr->CoarseMixFreq = 0x20; + + /* Identify CoarseMixFreq and CoarseMixerMode */ + if (ReadReg == XRFDC_CRSE_MIX_BYPASS) { + if (ReadReg_Mix1 == XRFDC_CRSE_MIX_BYPASS) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_BYPASS; + CoarseMixerMode = XRFDC_MIXER_MODE_C2C; + } else if (ReadReg_Mix1 == XRFDC_CRSE_MIX_OFF) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_BYPASS; + CoarseMixerMode = XRFDC_MIXER_MODE_R2R; + if (MixerConfigPtr->MixerMode == XRFDC_MIXER_MODE_R2C) { + CoarseMixerMode = XRFDC_MIXER_MODE_R2C; + } + } + } + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + if ((ReadReg_Mix1 == XRFDC_CRSE_MIX_I_Q_FSBYTWO) && + (ReadReg == XRFDC_CRSE_MIX_I_Q_FSBYTWO)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO; + CoarseMixerMode = XRFDC_MIXER_MODE_C2C; + } else if ((ReadReg_Mix1 == XRFDC_CRSE_MIX_OFF) && + (ReadReg == XRFDC_CRSE_MIX_I_Q_FSBYTWO)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO; + CoarseMixerMode = XRFDC_MIXER_MODE_R2C; + } + } else { + if (ReadReg == XRFDC_CRSE_4GSPS_ODD_FSBYTWO) { + if (ReadReg_Mix1 == XRFDC_CRSE_4GSPS_ODD_FSBYTWO) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO; + CoarseMixerMode = XRFDC_MIXER_MODE_C2C; + } else if (ReadReg_Mix1 == XRFDC_CRSE_MIX_OFF) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO; + CoarseMixerMode = XRFDC_MIXER_MODE_R2C; + } + } + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + if ((ReadReg_Mix1 == XRFDC_CRSE_MIX_Q_FSBYFOUR) && + (ReadReg == XRFDC_CRSE_MIX_I_FSBYFOUR)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR; + CoarseMixerMode = XRFDC_MIXER_MODE_C2C; + } else if ((ReadReg_Mix1 == + XRFDC_CRSE_MIX_R_Q_FSBYFOUR) && + (ReadReg == XRFDC_CRSE_MIX_R_I_MINFSBYFOUR)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR; + CoarseMixerMode = XRFDC_MIXER_MODE_R2C; + } + } else { + if ((ReadReg == XRFDC_CRSE_MIX_I_ODD_FSBYFOUR) && + (ReadReg_Mix1 == XRFDC_CRSE_MIX_Q_ODD_FSBYFOUR)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR; + CoarseMixerMode = XRFDC_MIXER_MODE_C2C; + } else if ((ReadReg == XRFDC_CRSE_MIX_OFF) && + (ReadReg_Mix1 == XRFDC_CRSE_MIX_Q_ODD_FSBYFOUR)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR; + CoarseMixerMode = XRFDC_MIXER_MODE_R2C; + } + + } + + if ((XRFdc_IsHighSpeedADC(InstancePtr, Tile_Id) == 0) || + (Type == XRFDC_DAC_TILE)) { + if ((ReadReg_Mix1 == XRFDC_CRSE_MIX_I_FSBYFOUR) && + (ReadReg == XRFDC_CRSE_MIX_Q_FSBYFOUR)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR; + CoarseMixerMode = XRFDC_MIXER_MODE_C2C; + } else if ((ReadReg_Mix1 == XRFDC_CRSE_MIX_R_Q_MINFSBYFOUR) && + (ReadReg == XRFDC_CRSE_MIX_R_I_MINFSBYFOUR)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR; + CoarseMixerMode = XRFDC_MIXER_MODE_R2C; + } + } else { + if ((ReadReg == XRFDC_CRSE_MIX_Q_ODD_FSBYFOUR) && + (ReadReg_Mix1 == XRFDC_CRSE_MIX_I_ODD_FSBYFOUR)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR; + CoarseMixerMode = XRFDC_MIXER_MODE_C2C; + } else if ((ReadReg == XRFDC_CRSE_MIX_OFF) && + (ReadReg_Mix1 == XRFDC_CRSE_MIX_I_ODD_FSBYFOUR)) { + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR; + CoarseMixerMode = XRFDC_MIXER_MODE_R2C; + } + + } + + if ((ReadReg == XRFDC_CRSE_MIX_OFF) && (ReadReg_Mix1 == + XRFDC_CRSE_MIX_OFF)) { + MixerSettingsPtr->CoarseMixFreq = XRFDC_COARSE_MIX_OFF; + CoarseMixerMode = XRFDC_MIXER_MODE_C2C; + } + if (MixerSettingsPtr->CoarseMixFreq == 0x20U) { + metal_log(METAL_LOG_ERROR, + "\n Coarse mixer settings not match any of the modes %s\r\n", + __func__); + } + if ((MixerConfigPtr->MixerMode == XRFDC_MIXER_MODE_C2R) && + (CoarseMixerMode == XRFDC_MIXER_MODE_C2C)) { + CoarseMixerMode = XRFDC_MIXER_MODE_C2R; + } + + /* Identify FineMixerMode */ + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, XRFDC_MXR_MODE_OFFSET, + (XRFDC_EN_I_IQ_MASK | XRFDC_EN_Q_IQ_MASK)); + if (ReadReg == 0xFU) { + FineMixerMode = XRFDC_MIXER_MODE_C2C; + } else if (ReadReg == 0x3U) { + FineMixerMode = XRFDC_MIXER_MODE_C2R; + } else if (ReadReg == 0x5U) { + FineMixerMode = XRFDC_MIXER_MODE_R2C; + } else { + FineMixerMode = XRFDC_MIXER_MODE_OFF; + } + + if (FineMixerMode == XRFDC_MIXER_MODE_OFF) { + MixerSettingsPtr->MixerType = XRFDC_MIXER_TYPE_COARSE; + MixerSettingsPtr->MixerMode = CoarseMixerMode; + } else { + MixerSettingsPtr->MixerType = XRFDC_MIXER_TYPE_FINE; + MixerSettingsPtr->MixerMode = FineMixerMode; + } + + /* Identify Fine Mixer Scale */ + ReadReg = XRFdc_RDReg(InstancePtr, BaseAddr, + XRFDC_MXR_MODE_OFFSET, XRFDC_FINE_MIX_SCALE_MASK); + if (InstancePtr->UpdateMixerScale == 0x0U) { + MixerSettingsPtr->FineMixerScale = + XRFDC_MIXER_SCALE_AUTO; + } else if ((ReadReg != 0U) && + (InstancePtr->UpdateMixerScale == 0x1U)) { + MixerSettingsPtr->FineMixerScale = + XRFDC_MIXER_SCALE_1P0; + } else if (InstancePtr->UpdateMixerScale == 0x1U) { + MixerSettingsPtr->FineMixerScale = + XRFDC_MIXER_SCALE_0P7; + } else { + metal_log(METAL_LOG_ERROR, + "\n Invalid Fine mixer scale in %s\r\n", __func__); + Status = XRFDC_FAILURE; + goto RETURN_PATH; + } + + /* Phase Offset */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_NCO_PHASE_UPP_OFFSET); + PhaseOffset = ReadReg << XRFDC_NCO_PHASE_UPP_SHIFT; + PhaseOffset |= XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_NCO_PHASE_LOW_OFFSET); + PhaseOffset &= XRFDC_NCO_PHASE_MASK; + PhaseOffset = ((PhaseOffset << 14) >> 14); + MixerSettingsPtr->PhaseOffset = ((PhaseOffset * 180.0) / + XRFDC_NCO_PHASE_MULTIPLIER); + + /* NCO Frequency */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_NCO_FQWD_UPP_OFFSET); + Freq = ReadReg << XRFDC_NCO_FQWD_UPP_SHIFT; + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_NCO_FQWD_MID_OFFSET); + Freq |= ReadReg << XRFDC_NCO_FQWD_MID_SHIFT; + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_ADC_NCO_FQWD_LOW_OFFSET); + Freq |= ReadReg; + Freq &= XRFDC_NCO_FQWD_MASK; + Freq = (Freq << 16) >> 16; + if (Freq < 0) { + MixerSettingsPtr->Freq = ((Freq * SamplingRate) / + XRFDC_NCO_FREQ_MIN_MULTIPLIER); + } else { + MixerSettingsPtr->Freq = ((Freq * SamplingRate) / + XRFDC_NCO_FREQ_MULTIPLIER); + } + + /* Event Source */ + ReadReg = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_NCO_UPDT_OFFSET); + MixerSettingsPtr->EventSource = ReadReg & XRFDC_NCO_UPDT_MODE_MASK; + + /* Update NCO, CoarseMix freq based on calibration mode */ + NCOFreq = MixerConfigPtr->Freq; + if (Type == XRFDC_ADC_TILE) { + Status = XRFdc_GetCalibrationMode(InstancePtr, Tile_Id, + Block, &CalibrationMode); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + + if (CalibrationMode == XRFDC_CALIB_MODE1) { + switch (MixerSettingsPtr->CoarseMixFreq) { + case XRFDC_COARSE_MIX_BYPASS: + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO; + break; + case XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR: + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR; + break; + case XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_TWO: + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_BYPASS; + break; + case XRFDC_COARSE_MIX_MIN_SAMPLE_FREQ_BY_FOUR: + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_SAMPLE_FREQ_BY_FOUR; + break; + default: + MixerSettingsPtr->CoarseMixFreq = + XRFDC_COARSE_MIX_OFF; + break; + } + NCOFreq = (MixerConfigPtr->Freq - + (SamplingRate / 2.0)); + } + } + + if ((NCOFreq > (SamplingRate / 2.0)) || + (NCOFreq < -(SamplingRate / 2.0))) { + Status = XRFdc_GetNyquistZone(InstancePtr, + Type, Tile_Id, Block, &NyquistZone); + if (Status != XRFDC_SUCCESS) { + return XRFDC_FAILURE; + } + + if ((NyquistZone == XRFDC_EVEN_NYQUIST_ZONE) && + (MixerSettingsPtr->Freq != 0)) { + MixerSettingsPtr->Freq *= -1; + } + + do { + if (NCOFreq < -(SamplingRate / 2.0)) { + NCOFreq += SamplingRate; + MixerSettingsPtr->Freq -= SamplingRate; + } + if (NCOFreq > (SamplingRate / 2.0)) { + NCOFreq -= SamplingRate; + MixerSettingsPtr->Freq += SamplingRate; + } + } while ((NCOFreq > (SamplingRate / 2.0)) || + (NCOFreq < -(SamplingRate / 2.0))); + } + if ((Type == XRFDC_ADC_TILE) && + (CalibrationMode == XRFDC_CALIB_MODE1)) { + MixerSettingsPtr->Freq += (SamplingRate / 2.0); + } + + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; + +} + +/** @} */ diff --git a/mpm/lib/rfdc/xrfdc_mts.c b/mpm/lib/rfdc/xrfdc_mts.c new file mode 100644 index 000000000..00a1abc85 --- /dev/null +++ b/mpm/lib/rfdc/xrfdc_mts.c @@ -0,0 +1,1308 @@ +/****************************************************************************** +* +* Copyright (C) 2018-2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc_mts.c +* @addtogroup xrfdc_v6_0 +* @{ +* +* Contains the multi tile sync functions of the XRFdc driver. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 3.1 jm 01/24/18 Initial release +* 3.2 jm 03/12/18 Fixed DAC latency calculation. +* jm 03/12/18 Added support for reloading DTC scans. +* jm 03/12/18 Add option to configure sysref capture after MTS. +* 4.0 sk 04/09/18 Added API to enable/disable the sysref. +* rk 04/17/18 Adjust calculated latency by sysref period, where doing +* so results in closer alignment to the target latency. +* 5.0 sk 08/03/18 Fixed MISRAC warnings. +* sk 08/03/18 Check for Block0 enable for tiles participating in MTS. +* sk 08/24/18 Reorganize the code to improve readability and +* optimization. +* 5.1 cog 01/29/19 Replace structure reference ADC checks with +* function. +* 6.0 cog 02/17/19 Added XRFdc_GetMTSEnable API. +* +* </pre> +* +******************************************************************************/ + +/***************************** Include Files *********************************/ +#include "mpm/rfdc/xrfdc_mts.h" + +/************************** Constant Definitions *****************************/ + + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +static void XRFdc_MTS_Sysref_TRx(XRFdc *InstancePtr, u32 Enable); +static void XRFdc_MTS_Sysref_Ctrl(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Is_PLL, u32 Enable_Cap, u32 Enable_Div_Reset); +static u32 XRFdc_MTS_Sysref_Dist(XRFdc *InstancePtr, int Num_DAC); +static u32 XRFdc_MTS_Sysref_Count(XRFdc *InstancePtr, u32 Type, u32 Count_Val); +static u32 XRFdc_MTS_Dtc_Scan(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + XRFdc_MTS_DTC_Settings *SettingsPtr); +static u32 XRFdc_MTS_Dtc_Code(XRFdc *InstancePtr, u32 Type, u32 BaseAddr, + u32 SRCtrlAddr, u32 DTCAddr, u16 SRctl, u16 SRclr_m, u32 Code); +static u32 XRFdc_MTS_Dtc_Calc(u32 Type, u32 Tile_Id, + XRFdc_MTS_DTC_Settings *SettingsPtr, u8 *FlagsPtr); +static void XRFdc_MTS_Dtc_Flag_Debug(u8 *FlagsPtr, u32 Type, u32 Tile_Id, + u32 Target, u32 Picked); +static void XRFdc_MTS_FIFOCtrl(XRFdc *InstancePtr, u32 Type, u32 FIFO_Mode, + u32 Tiles_To_Clear); +static u32 XRFdc_MTS_GetMarker(XRFdc *InstancePtr, u32 Type, u32 Tiles, + XRFdc_MTS_Marker *MarkersPtr, int Marker_Delay); +static void XRFdc_MTS_Marker_Read(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 FIFO_Id, u32 *CountPtr, u32 *LocPtr, u32 *DonePtr); +static u32 XRFdc_MTS_Latency(XRFdc *InstancePtr, u32 Type, + XRFdc_MultiConverter_Sync_Config *ConfigPtr, XRFdc_MTS_Marker *MarkersPtr); + +/*****************************************************************************/ +/** +* +* This API enables the master tile sysref Tx/Rx +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Enable the master tile sysref for Tx/Rx, valid values are 0 and 1. +* +* @return +* - None. +* +* @note None +* +******************************************************************************/ +static void XRFdc_MTS_Sysref_TRx(XRFdc *InstancePtr, u32 Enable) +{ + u32 BaseAddr; + u32 Data; + + BaseAddr = XRFDC_DRP_BASE(XRFDC_DAC_TILE, 0) + XRFDC_HSCOM_ADDR; + Data = (Enable != 0U) ? 0xFFFFU : 0U; + + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MTS_SRCAP_T1, + XRFDC_MTS_SRCAP_EN_TRX_M, Data); +} + +/*****************************************************************************/ +/** +* +* This API Control SysRef Capture Settings +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Is_PLL Valid values are 0 and 1. +* @param Enable_Cap Valid values are 0 and 1. +* @param Enable_Div_Reset Valid values are 0 and 1. +* +* @return +* - None. +* +* @note None +* +******************************************************************************/ +static void XRFdc_MTS_Sysref_Ctrl(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 Is_PLL, u32 Enable_Cap, u32 Enable_Div_Reset) +{ + u32 BaseAddr; + u16 RegData; + + RegData = 0U; + BaseAddr = XRFDC_DRP_BASE(Type, Tile_Id) + XRFDC_HSCOM_ADDR; + + /* Write some bits to ensure sysref is in the right mode */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MTS_SRCAP_T1, + XRFDC_MTS_SRCAP_INIT_M, 0U); + + if (Is_PLL != 0U) { + /* PLL Cap */ + RegData = (Enable_Cap != 0U) ? XRFDC_MTS_SRCAP_PLL_M : 0U; + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MTS_SRCAP_PLL, + XRFDC_MTS_SRCAP_PLL_M, RegData); + } else { + /* Analog Cap disable */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MTS_SRCAP_T1, + XRFDC_MTS_SRCAP_T1_EN, 0U); + + /* Analog Divider */ + RegData = (Enable_Div_Reset != 0U) ? 0U : XRFDC_MTS_SRCAP_T1_RST; + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MTS_SRCAP_T1, + XRFDC_MTS_SRCAP_T1_RST, RegData); + + /* Digital Divider */ + RegData = (Enable_Div_Reset != 0U) ? 0U : XRFDC_MTS_SRCAP_DIG_M; + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MTS_SRCAP_DIG, + XRFDC_MTS_SRCAP_DIG_M, RegData); + + /* Set SysRef Cap Clear */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MTS_SRCAP_T1, + XRFDC_MTS_SRCLR_T1_M, XRFDC_MTS_SRCLR_T1_M); + + /* Analog Cap enable */ + RegData = (Enable_Cap != 0U) ? XRFDC_MTS_SRCAP_T1_EN : 0U; + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MTS_SRCAP_T1, + XRFDC_MTS_SRCAP_T1_EN, RegData); + + /* Unset SysRef Cap Clear */ + XRFdc_ClrSetReg(InstancePtr, BaseAddr, XRFDC_MTS_SRCAP_T1, + XRFDC_MTS_SRCLR_T1_M, 0U); + } +} + +/*****************************************************************************/ +/** +* +* This API Update SysRef Distribution between tiles +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Num_DAC is number of DAC tiles +* +* @return +* - XRFDC_MTS_OK if successful. +* - XRFDC_MTS_NOT_SUPPORTED +* +* @note None +* +******************************************************************************/ +static u32 XRFdc_MTS_Sysref_Dist(XRFdc *InstancePtr, int Num_DAC) +{ + + if (Num_DAC < 0) { + /* Auto-detect. Only 2 types Supported - 2GSPS ADCs, 4GSPS ADCs */ + if (XRFdc_IsHighSpeedADC(InstancePtr,0) != 0U) { + Num_DAC = 2; + } else { + Num_DAC = 4; + } + } + + if (Num_DAC == XRFDC_NUM_OF_TILES2) { + /* 2 DACs, 4ADCs */ + XRFdc_WriteReg16(InstancePtr, XRFDC_DAC_TILE_DRP_ADDR(0U), + XRFDC_MTS_SRDIST, 0xC980U); + XRFdc_WriteReg16(InstancePtr, XRFDC_DAC_TILE_DRP_ADDR(1U), + XRFDC_MTS_SRDIST, 0x0100U); + XRFdc_WriteReg16(InstancePtr, XRFDC_ADC_TILE_DRP_ADDR(3U), + XRFDC_MTS_SRDIST, 0x1700U); + } else if (Num_DAC == XRFDC_NUM_OF_TILES4) { + /* 4 DACs, 4ADCs */ + XRFdc_WriteReg16(InstancePtr, XRFDC_DAC_TILE_DRP_ADDR(0U), + XRFDC_MTS_SRDIST, 0xCA80U); + XRFdc_WriteReg16(InstancePtr, XRFDC_DAC_TILE_DRP_ADDR(1U), + XRFDC_MTS_SRDIST, 0x2400U); + XRFdc_WriteReg16(InstancePtr, XRFDC_DAC_TILE_DRP_ADDR(2U), + XRFDC_MTS_SRDIST, 0x0980U); + XRFdc_WriteReg16(InstancePtr, XRFDC_DAC_TILE_DRP_ADDR(3U), + XRFDC_MTS_SRDIST, 0x0100U); + XRFdc_WriteReg16(InstancePtr, XRFDC_ADC_TILE_DRP_ADDR(3U), + XRFDC_MTS_SRDIST, 0x0700U); + } else { + return XRFDC_MTS_NOT_SUPPORTED; + } + + XRFdc_WriteReg16(InstancePtr, XRFDC_ADC_TILE_DRP_ADDR(0U), + XRFDC_MTS_SRDIST, 0x0280U); + XRFdc_WriteReg16(InstancePtr, XRFDC_ADC_TILE_DRP_ADDR(1U), + XRFDC_MTS_SRDIST, 0x0600U); + XRFdc_WriteReg16(InstancePtr, XRFDC_ADC_TILE_DRP_ADDR(2U), + XRFDC_MTS_SRDIST, 0x8880U); + + return XRFDC_MTS_OK; +} + +/*****************************************************************************/ +/** +* +* This API Wait for a number of sysref's to be captured +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Count_Val to wait for a number of sysref's to be captured. +* +* @return +* - XRFDC_MTS_OK if successful. +* - XRFDC_MTS_TIMEOUT if timeout occurs. +* +* @note None +* +******************************************************************************/ +static u32 XRFdc_MTS_Sysref_Count(XRFdc *InstancePtr, u32 Type, u32 Count_Val) +{ + u32 RegData; + u32 Timeout; + u32 Shift; + + RegData = (Type == XRFDC_DAC_TILE) ? 0x2U : 0x1U; + Shift = (Type == XRFDC_DAC_TILE) ? 8U : 0U; + + /* Start counter */ + XRFdc_WriteReg(InstancePtr, 0U, XRFDC_MTS_SRCOUNT_CTRL, RegData); + + /* Check counter with timeout in case sysref is not active */ + Timeout = 0U; + while (Timeout < XRFDC_MTS_SRCOUNT_TIMEOUT) { + RegData = XRFdc_ReadReg(InstancePtr, 0U, XRFDC_MTS_SRCOUNT_VAL); + RegData = ((RegData >> Shift) & XRFDC_MTS_SRCOUNT_M); + if (RegData >= Count_Val) { + break; + } + Timeout++; + } + + if (Timeout >= XRFDC_MTS_SRCOUNT_TIMEOUT) { + metal_log(METAL_LOG_ERROR, + "PL SysRef Timeout - PL SysRef not active: %d\n in %s\n", + Timeout, __func__); + return XRFDC_MTS_TIMEOUT; + } + + return XRFDC_MTS_OK; +} + +/*****************************************************************************/ +/** +* +* This API print the DTC scan results +* +* +* @param FlagsPtr is for internal usage. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param Target is for internal usage. +* @param Picked is for internal usage. +* +* @return None +* +* @note None +* +******************************************************************************/ +static void XRFdc_MTS_Dtc_Flag_Debug(u8 *FlagsPtr, u32 Type, u32 Tile_Id, + u32 Target, u32 Picked) +{ + u32 Index; + char buf[XRFDC_MTS_NUM_DTC+1]; + + for (Index = 0U; Index < XRFDC_MTS_NUM_DTC; Index++) { + if (Index == Picked) { + buf[Index] = '*'; + } else if (Index == Target) { + buf[Index] = '#'; + } else { + buf[Index] = '0' + FlagsPtr[Index]; + } + } + buf[XRFDC_MTS_NUM_DTC] = '\0'; + metal_log(METAL_LOG_INFO, "%s%d: %s\n", + (Type == XRFDC_DAC_TILE) ? "DAC" : "ADC", Tile_Id, buf); + + (void)buf; + (void)Type; + (void)Tile_Id; + +} + +/*****************************************************************************/ +/** +* +* This API Calculate the best DTC code to use +* +* +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param SettingsPtr dtc settings structure. +* @param FlagsPtr is for internal usage. +* +* @return +* - XRFDC_MTS_OK if successful. +* - XRFDC_MTS_NOT_SUPPORTED if MTS is not supported. +* +* @note None +* +******************************************************************************/ +static u32 XRFdc_MTS_Dtc_Calc(u32 Type, u32 Tile_Id, + XRFdc_MTS_DTC_Settings *SettingsPtr, u8 *FlagsPtr) +{ + u32 Index, Status, Num_Found; + int Last, Current_Gap, Max_Overlap, Overlap_Cnt; + int Min_Gap, Max_Gap, Diff, Min_Diff, Min_Range, Val, Target; + u8 Min_Gap_Allowed; + int Codes[XRFDC_MTS_MAX_CODE] = {0}; + + Min_Gap_Allowed = (SettingsPtr->IsPLL != 0U) ? XRFDC_MTS_MIN_GAP_PLL : + XRFDC_MTS_MIN_GAP_T1; + Status = XRFDC_MTS_OK; + + /* Scan the flags and find candidate DTC codes */ + Num_Found = 0U; + Max_Gap = 0; + Min_Gap = XRFDC_MTS_NUM_DTC; + Max_Overlap = 0; + Overlap_Cnt = 0; + Last = -1; + FlagsPtr[XRFDC_MTS_NUM_DTC] = 1; + for (Index = 0U; Index <= XRFDC_MTS_NUM_DTC; Index++) { + Current_Gap = Index-Last; + if (FlagsPtr[Index] != 0) { + if (Current_Gap > Min_Gap_Allowed) { + Codes[Num_Found] = Last + (Current_Gap / 2); + Num_Found++; + /* Record max/min gaps */ + Current_Gap--; + if (Current_Gap > Max_Gap) { + Max_Gap = Current_Gap; + } + if (Current_Gap < Min_Gap) { + Min_Gap = Current_Gap; + } + } + Last = Index; + } + /* check for the longest run of overlapping codes */ + if (FlagsPtr[Index] == 3U) { + Overlap_Cnt++; + if (Overlap_Cnt > Max_Overlap) { + Max_Overlap = Overlap_Cnt; + } + } else { + Overlap_Cnt = 0; + } + } + + /* Record some stats */ + SettingsPtr->Num_Windows[Tile_Id] = Num_Found; + SettingsPtr->Max_Gap[Tile_Id] = Max_Gap; + SettingsPtr->Min_Gap[Tile_Id] = Min_Gap; + SettingsPtr->Max_Overlap[Tile_Id] = Max_Overlap; + + /* Calculate the best code */ + if (SettingsPtr->Scan_Mode == XRFDC_MTS_SCAN_INIT) { + /* Initial scan */ + if (Tile_Id == SettingsPtr->RefTile) { + /* RefTile: Get the code closest to the target */ + Target = XRFDC_MTS_REF_TARGET; + SettingsPtr->Target[Tile_Id] = XRFDC_MTS_REF_TARGET; + Min_Diff = XRFDC_MTS_NUM_DTC; + /* scan all codes to find the closest */ + for (Index = 0U; Index < Num_Found; Index++) { + Diff = abs(Target - Codes[Index]); + if (Diff < Min_Diff) { + Min_Diff = Diff; + SettingsPtr->DTC_Code[Tile_Id] = Codes[Index]; + } + metal_log(METAL_LOG_DEBUG, + "Target %d, DTC Code %d, Diff %d, Min %d\n", Target, + Codes[Index], Diff, Min_Diff); + } + /* set the reference code as the target for the other tiles */ + for (Index = 0U; Index < 4U; Index++) { + if (Index != Tile_Id) { + SettingsPtr->Target[Index] = SettingsPtr->DTC_Code[Tile_Id]; + } + } + metal_log(METAL_LOG_DEBUG, + "RefTile (%d): DTC Code Target %d, Picked %d\n", Tile_Id, + Target, SettingsPtr->DTC_Code[Tile_Id]); + + } else { + /* + * Other Tiles: Get the code that minimises the total range of codes + * compute the range of the existing dtc codes + */ + Max_Gap = 0; + Min_Gap = XRFDC_MTS_NUM_DTC; + for (Index = XRFDC_TILE_ID0; Index < XRFDC_TILE_ID4; Index++) { + Val = SettingsPtr->DTC_Code[Index]; + if ((Val != -1) && (Val > Max_Gap)) { + Max_Gap = Val; + } + if ((Val != -1) && (Val < Min_Gap)) { + Min_Gap = Val; + } + } + metal_log(METAL_LOG_DEBUG, + "Tile (%d): Max/Min %d/%d, Range %d\n", Tile_Id, Max_Gap, + Min_Gap, Max_Gap-Min_Gap); + Min_Range = XRFDC_MTS_NUM_DTC; + for (Index = 0U; Index < Num_Found; Index++) { + Val = Codes[Index]; + Diff = Max_Gap - Min_Gap; + if (Val < Min_Gap) { + Diff = Max_Gap - Val; + } + if (Val > Max_Gap) { + Diff = Val - Min_Gap; + } + if (Diff <= Min_Range) { + Min_Range = Diff; + SettingsPtr->DTC_Code[Tile_Id] = Val; + } + metal_log(METAL_LOG_DEBUG, + "Tile (%d): Code %d, New-Range: %d, Min-Range: %d\n", + Tile_Id, Val, Diff, Min_Range); + } + metal_log(METAL_LOG_DEBUG, + "Tile (%d): Code %d, Range Prev %d, New %d\n", Tile_Id, + SettingsPtr->DTC_Code[Tile_Id], Max_Gap-Min_Gap, Min_Range); + } + } else { + /* Reload the results of an initial scan to seed a new scan */ + if (Tile_Id == SettingsPtr->RefTile) { + /* RefTile: Get code closest to the target */ + Target = SettingsPtr->Target[Tile_Id]; + } else { + Target = SettingsPtr->DTC_Code[SettingsPtr->RefTile] + + SettingsPtr->Target[Tile_Id] - SettingsPtr->Target[SettingsPtr->RefTile]; + } + Min_Diff = XRFDC_MTS_NUM_DTC; + /* scan all codes to find the closest */ + for (Index = 0U; Index < Num_Found; Index++) { + Diff = abs(Target - Codes[Index]); + if (Diff < Min_Diff) { + Min_Diff = Diff; + SettingsPtr->DTC_Code[Tile_Id] = Codes[Index]; + } + metal_log(METAL_LOG_DEBUG, + "Reload Target %d, DTC Code %d, Diff %d, Min %d\n", Target, + Codes[Index], Diff, Min_Diff); + } + } + + /* Print some debug info */ + XRFdc_MTS_Dtc_Flag_Debug(FlagsPtr, Type, Tile_Id, SettingsPtr->Target[Tile_Id], + SettingsPtr->DTC_Code[Tile_Id]); + + return Status; +} + +/*****************************************************************************/ +/** +* +* This API Set a DTC code and wait for it to be updated. Return early/late +* flags, if set +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param BaseAddr is for internal usage. +* @param SRCtrlAddr is for internal usage. +* @param DTCAddr is for internal usage. +* @param SRctl is for internal usage. +* @param SRclr_m is for internal usage. +* @param Code is for internal usage. +* +* @return +* - XRFDC_MTS_OK if successful. +* - XRFDC_MTS_TIMEOUT if timeout occurs. +* +* @note None +* +******************************************************************************/ +static u32 XRFdc_MTS_Dtc_Code(XRFdc *InstancePtr, u32 Type, u32 BaseAddr, + u32 SRCtrlAddr, u32 DTCAddr, u16 SRctl, u16 SRclr_m, u32 Code) +{ + u32 Status; + + /* set the DTC code */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, DTCAddr, Code); + + /* set sysref cap clear */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, SRCtrlAddr, SRctl | SRclr_m); + + /* unset sysref cap clear */ + XRFdc_WriteReg16(InstancePtr, BaseAddr, SRCtrlAddr, SRctl); + + Status = XRFdc_MTS_Sysref_Count(InstancePtr, Type, XRFDC_MTS_DTC_COUNT); + + return Status; +} + +/*****************************************************************************/ +/** +* +* This API Scan the DTC codes and determine the optimal capture code for +* both PLL and T1 cases +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param SettingsPtr dtc settings structure. +* +* @return +* - XRFDC_MTS_OK if successful. +* - XRFDC_MTS_TIMEOUT if timeout occurs. +* +* @note None +* +******************************************************************************/ +static u32 XRFdc_MTS_Dtc_Scan (XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + XRFdc_MTS_DTC_Settings *SettingsPtr) +{ + u32 Status; + u32 BaseAddr; + u32 SRCtrlAddr; + u32 DTCAddr; + u8 Flags[XRFDC_MTS_NUM_DTC+1]; + u16 SRctl; + u16 SRclr_m; + u16 Flag_s; + u32 Index; + + BaseAddr = XRFDC_DRP_BASE(Type, Tile_Id) + XRFDC_HSCOM_ADDR; + Status = XRFDC_MTS_OK; + + /* Enable SysRef Capture and Disable Divide Reset */ + XRFdc_MTS_Sysref_Ctrl(InstancePtr, Type, Tile_Id, SettingsPtr->IsPLL, 1, 0); + SRCtrlAddr = (SettingsPtr->IsPLL != 0U) ? XRFDC_MTS_SRCAP_PLL : XRFDC_MTS_SRCAP_T1; + DTCAddr = (SettingsPtr->IsPLL != 0U) ? XRFDC_MTS_SRDTC_PLL : XRFDC_MTS_SRDTC_T1; + SRclr_m = (SettingsPtr->IsPLL != 0U) ? XRFDC_MTS_SRCLR_PLL_M : XRFDC_MTS_SRCLR_T1_M; + Flag_s = (SettingsPtr->IsPLL != 0U) ? XRFDC_MTS_SRFLAG_PLL : XRFDC_MTS_SRFLAG_T1; + + SRctl = XRFdc_ReadReg16(InstancePtr, BaseAddr, SRCtrlAddr) & ~SRclr_m; + + for (Index = 0U; Index < XRFDC_MTS_NUM_DTC; Index++) { + Flags[Index] = 0U; + } + for (Index = 0U; (Index < XRFDC_MTS_NUM_DTC) && (Status == XRFDC_MTS_OK); Index++) { + Status |= XRFdc_MTS_Dtc_Code(InstancePtr, Type, BaseAddr, + SRCtrlAddr, DTCAddr, SRctl, SRclr_m, Index); + Flags[Index] = (XRFdc_ReadReg16(InstancePtr, BaseAddr, XRFDC_MTS_SRFLAG) >> + Flag_s) & 0x3U; + } + + /* Calculate the best DTC code */ + (void)XRFdc_MTS_Dtc_Calc(Type, Tile_Id, SettingsPtr, Flags); + + /* Program the calculated code */ + if (SettingsPtr->DTC_Code[Tile_Id] == -1) { + metal_log(METAL_LOG_ERROR, + "Unable to capture analog SysRef safely on %s tile %d\n" + , (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", Tile_Id); + Status |= XRFDC_MTS_DTC_INVALID; + } else { + (void)XRFdc_MTS_Dtc_Code(InstancePtr, Type, BaseAddr, SRCtrlAddr, DTCAddr, + SRctl, SRclr_m, SettingsPtr->DTC_Code[Tile_Id]); + } + + if (SettingsPtr->IsPLL != 0U) { + /* PLL - Disable SysRef Capture */ + XRFdc_MTS_Sysref_Ctrl(InstancePtr, Type, Tile_Id, 1, 0, 0); + } else { + /* T1 - Reset Dividers */ + XRFdc_MTS_Sysref_Ctrl(InstancePtr, Type, Tile_Id, 0, 1, 1); + Status |= XRFdc_MTS_Sysref_Count(InstancePtr, Type, + XRFDC_MTS_DTC_COUNT); + XRFdc_MTS_Sysref_Ctrl(InstancePtr, Type, Tile_Id, 0, 1, 0); + } + + return Status; +} + +/*****************************************************************************/ +/** +* +* This API Control the FIFO enable for the group. If Tiles_to_clear has bits +* set, the FIFOs of those tiles will have their FIFO flags cleared. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param FIFO_Mode is fifo mode. +* @param Tiles_To_Clear bits set, FIFO flags will be cleared for those tiles. +* +* @return None +* +* @note None +* +******************************************************************************/ +static void XRFdc_MTS_FIFOCtrl (XRFdc *InstancePtr, u32 Type, u32 FIFO_Mode, + u32 Tiles_To_Clear) +{ + u32 RegAddr; + u32 BaseAddr; + u32 Tile_Id; + u32 Block_Id; + + /* Clear the FIFO Flags */ + RegAddr = (Type == XRFDC_ADC_TILE) ? XRFDC_ADC_FABRIC_ISR_OFFSET : + XRFDC_DAC_FABRIC_ISR_OFFSET; + for (Tile_Id = XRFDC_TILE_ID0; Tile_Id < XRFDC_TILE_ID4; Tile_Id++) { + if (((1U << Tile_Id) & Tiles_To_Clear) != 0U) { + for (Block_Id = XRFDC_BLK_ID0; Block_Id < XRFDC_BLK_ID4; Block_Id++) { + BaseAddr = XRFDC_DRP_BASE(Type, Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(Block_Id); + XRFdc_WriteReg16(InstancePtr, BaseAddr, RegAddr, + XRFDC_IXR_FIFOUSRDAT_MASK); + } + } + } + + /* Enable the FIFOs */ + RegAddr = (Type == XRFDC_ADC_TILE) ? XRFDC_MTS_FIFO_CTRL_ADC : + XRFDC_MTS_FIFO_CTRL_DAC; + XRFdc_WriteReg(InstancePtr, 0, RegAddr, FIFO_Mode); +} + +/*****************************************************************************/ +/** +* +* This API Read-back the marker data for an ADC or DAC +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tile_Id Valid values are 0-3. +* @param FIFO_Id is FIFO number. +* @param Count is for internal usage. +* @param Loc is for internal usage. +* @param Done is for internal usage. +* +* @return +* - None. +* +* @note None +* +******************************************************************************/ +static void XRFdc_MTS_Marker_Read(XRFdc *InstancePtr, u32 Type, u32 Tile_Id, + u32 FIFO_Id, u32 *CountPtr, u32 *LocPtr, u32 *DonePtr) +{ + u32 BaseAddr; + u32 RegData = 0x0; + + if (Type == XRFDC_ADC_TILE) { + BaseAddr = XRFDC_DRP_BASE(Type, Tile_Id) - 0x2000; + RegData = XRFdc_ReadReg(InstancePtr, BaseAddr, + XRFDC_MTS_ADC_MARKER_CNT+(FIFO_Id << 2)); + *CountPtr = XRFDC_MTS_FIELD(RegData, XRFDC_MTS_AMARK_CNT_M, 0); + *LocPtr = XRFDC_MTS_FIELD(RegData, XRFDC_MTS_AMARK_LOC_M, + XRFDC_MTS_AMARK_LOC_S); + *DonePtr = XRFDC_MTS_FIELD(RegData, XRFDC_MTS_AMARK_DONE_M, + XRFDC_MTS_AMARK_DONE_S); + } else { + BaseAddr = XRFDC_DRP_BASE(Type, Tile_Id) + + XRFDC_BLOCK_ADDR_OFFSET(FIFO_Id); + *CountPtr = XRFdc_ReadReg(InstancePtr, BaseAddr, + XRFDC_MTS_DAC_MARKER_CNT); + *LocPtr = XRFdc_ReadReg(InstancePtr, BaseAddr, + XRFDC_MTS_DAC_MARKER_LOC); + *DonePtr = 1; + } + metal_log(METAL_LOG_DEBUG, + "Marker Read Tile %d, FIFO %d - %08X = %04X: count=%d, loc=%d," + "done=%d\n", Tile_Id, FIFO_Id, BaseAddr, RegData, *CountPtr, + *LocPtr, *DonePtr); +} + +/*****************************************************************************/ +/** +* +* This API Run the marker counter and read the results +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param Tiles is tiles to get marker +* @param MarkersPtr mts marker structure. +* @param Marker_Delay is marker delay. +* +* @return +* - XRFDC_MTS_OK if successful. +* - XRFDC_MTS_TIMEOUT if timeout occurs. +* - XRFDC_MTS_MARKER_RUN +* - XRFDC_MTS_MARKER_MISM +* - +* +* @note None +* +******************************************************************************/ +static u32 XRFdc_MTS_GetMarker(XRFdc *InstancePtr, u32 Type, u32 Tiles, + XRFdc_MTS_Marker *MarkersPtr, int Marker_Delay) +{ + u32 Done; + u32 Count; + u32 Loc; + u32 Tile_Id; + u32 Block_Id; + u32 Status; + + Status = XRFDC_MTS_OK; + if (Type == XRFDC_ADC_TILE) { + /* Reset marker counter */ + XRFdc_WriteReg(InstancePtr, 0, XRFDC_MTS_ADC_MARKER, 1); + XRFdc_WriteReg(InstancePtr, 0, XRFDC_MTS_ADC_MARKER, 0); + } else { + /* + * SysRef Capture should be still active from the DTC Scan + * but set it anyway to be sure + */ + for (Tile_Id = XRFDC_TILE_ID0; Tile_Id < XRFDC_TILE_ID4; Tile_Id++) { + if (((1U << Tile_Id) & Tiles) != 0U) { + XRFdc_MTS_Sysref_Ctrl(InstancePtr, XRFDC_DAC_TILE, + Tile_Id, 0, 1, 0); + } + } + + /* Set marker delay */ + XRFdc_WriteReg(InstancePtr, 0, XRFDC_MTS_DAC_MARKER_CTRL, + Marker_Delay); + } + + /* Allow the marker counter to run */ + Status |= XRFdc_MTS_Sysref_Count(InstancePtr, Type, + XRFDC_MTS_MARKER_COUNT); + + /* Read master FIFO (FIFO0 in each Tile) */ + for (Tile_Id = XRFDC_TILE_ID0; Tile_Id < XRFDC_TILE_ID4; Tile_Id++) { + if (((1U << Tile_Id) & Tiles) != 0U) { + if (Type == XRFDC_DAC_TILE) { + /* Disable SysRef Capture before reading it */ + XRFdc_MTS_Sysref_Ctrl(InstancePtr, XRFDC_DAC_TILE, + Tile_Id, 0, 0, 0); + Status |= XRFdc_MTS_Sysref_Count(InstancePtr, Type, + XRFDC_MTS_MARKER_COUNT); + } + + XRFdc_MTS_Marker_Read(InstancePtr, Type, Tile_Id, 0, &Count, + &Loc, &Done); + MarkersPtr->Count[Tile_Id] = Count; + MarkersPtr->Loc[Tile_Id] = Loc; + metal_log(METAL_LOG_INFO, + "%s%d: Marker: - %d, %d\n", (Type == XRFDC_DAC_TILE) ? + "DAC":"ADC", Tile_Id, MarkersPtr->Count[Tile_Id], MarkersPtr->Loc[Tile_Id]); + + if ((!Done) != 0U) { + metal_log(METAL_LOG_ERROR, "Analog SysRef timeout," + "SysRef not detected on %s tile %d\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", Tile_Id); + Status |= XRFDC_MTS_MARKER_RUN; + } + + /* + * Check all enabled FIFOs agree with the master FIFO. + * This is optional. + */ + for (Block_Id = XRFDC_BLK_ID0; Block_Id < XRFDC_BLK_ID4; Block_Id++) { + if (XRFdc_IsFifoEnabled(InstancePtr, Type, Tile_Id, Block_Id) != 0U) { + XRFdc_MTS_Marker_Read(InstancePtr, Type, Tile_Id, Block_Id, + &Count, &Loc, &Done); + if ((MarkersPtr->Count[Tile_Id] != Count) || + (MarkersPtr->Loc[Tile_Id] != Loc)) { + metal_log(METAL_LOG_DEBUG, + "Tile %d, FIFO %d Marker != Expected: %d, %d vs" + "%d, %d\n", Tile_Id, Block_Id, MarkersPtr->Count[Tile_Id], + MarkersPtr->Loc[Tile_Id], Count, Loc); + metal_log(METAL_LOG_ERROR, + "SysRef capture mismatch on %s tile %d," + " PL SysRef may not have been" + " captured synchronously\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", Block_Id); + Status |= XRFDC_MTS_MARKER_MISM; + + } + } + } + } + } + + return Status; +} + +/*****************************************************************************/ +/** +* +* This API Calculate the absoulte/relative latencies +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param ConfigPtr is mts config structure. +* @param MarkersPtr is mts marker structure. +* +* @return +* - XRFDC_MTS_OK if successful. +* - XRFDC_MTS_DELAY_OVER +* - XRFDC_MTS_TARGET_LOW +* - +* +* @note Latency calculation will use Sysref frequency counters +* logic which will work with IP version 2.0.1 and above. +* +******************************************************************************/ +static u32 XRFdc_MTS_Latency(XRFdc *InstancePtr, u32 Type, + XRFdc_MultiConverter_Sync_Config *ConfigPtr, XRFdc_MTS_Marker *MarkersPtr) +{ + u32 Status, Fifo, Index, BaseAddr, RegAddr; + int Count_W, Loc_W, Latency, Offset, Max_Latency, Target, Delta; + int I_Part, F_Part, SysRefT1Period, LatencyDiff, LatencyOffset; + u32 RegData, SysRefFreqCntrDone; + int Target_Latency = -1; + int LatencyOffsetDiff; + u32 Factor = 1U; + u32 Write_Words = 0U; + u32 Read_Words = 1U; + + Status = XRFDC_MTS_OK; + if (Type == XRFDC_ADC_TILE) { + (void)XRFdc_GetDecimationFactor(InstancePtr, ConfigPtr->RefTile, 0, &Factor); + } else { + (void)XRFdc_GetInterpolationFactor(InstancePtr, ConfigPtr->RefTile, 0, &Factor); + (void)XRFdc_GetFabWrVldWords(InstancePtr, Type, ConfigPtr->RefTile, 0, &Write_Words); + } + (void)XRFdc_GetFabRdVldWords(InstancePtr, Type, ConfigPtr->RefTile, 0, &Read_Words); + Count_W = Read_Words * Factor; + Loc_W = Factor; + + metal_log(METAL_LOG_DEBUG, + "Count_W %d, loc_W %d\n", Count_W, Loc_W); + + /* Find the individual latencies */ + Max_Latency = 0; + + /* Determine relative SysRef frequency */ + RegData = XRFdc_ReadReg(InstancePtr, 0, XRFDC_MTS_SRFREQ_VAL); + if (Type == XRFDC_ADC_TILE) { + /* ADC SysRef frequency information contained in lower 16 bits */ + RegData = RegData & 0XFFFFU; + } else { + /* DAC SysRef frequency information contained in upper 16 bits */ + RegData = (RegData >> 16U) & 0XFFFFU; + } + + /* + * Ensure SysRef frequency counter has completed. + * Sysref frequency counters logic will work with IP version + * 2.0.1 and above. + */ + SysRefFreqCntrDone = RegData & 0x1U; + if (SysRefFreqCntrDone == 0U) { + metal_log(METAL_LOG_ERROR, "Error : %s SysRef frequency counter not yet done\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC"); + Status |= XRFDC_MTS_SYSREF_FREQ_NDONE; + /* Set SysRef period in terms of T1's will not be used */ + SysRefT1Period = 0; + } else { + SysRefT1Period = (RegData >> 1) * Count_W; + if (Type == XRFDC_DAC_TILE) { + /* + * DAC marker counter is on the tile clock domain so need + * to update SysRef period accordingly + */ + SysRefT1Period = (SysRefT1Period * Write_Words) / Read_Words; + } + metal_log(METAL_LOG_INFO, "SysRef period in terms of %s T1s = %d\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", SysRefT1Period); + } + + /* Work out the latencies */ + for (Index = XRFDC_TILE_ID0; Index < XRFDC_TILE_ID4; Index++) { + if (((1U << Index) & ConfigPtr->Tiles) != 0U) { + Latency = (MarkersPtr->Count[Index] * Count_W) + (MarkersPtr->Loc[Index] * Loc_W); + /* Set marker counter target on first tile */ + if (Target_Latency < 0) { + Target_Latency = ConfigPtr->Target_Latency; + if (Target_Latency < 0) { + Target_Latency = Latency; + } + metal_log(METAL_LOG_INFO, "%s target latency = %d\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", Target_Latency); + } + + /* + * Adjust reported counter values if offsetting by a SysRef + * period reduces distance between current and target latencies + */ + LatencyDiff = Target_Latency - Latency; + LatencyOffset = (LatencyDiff > 0) ? (Latency + SysRefT1Period) : + (Latency - SysRefT1Period); + LatencyOffsetDiff = Target_Latency - LatencyOffset; + if (abs(LatencyDiff) > abs(LatencyOffsetDiff)) { + Latency = LatencyOffset; + metal_log(METAL_LOG_INFO, "%s%d latency offset by a SysRef period to %d\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", Index, Latency); + } + ConfigPtr->Latency[Index] = Latency; + if (Latency > Max_Latency) { + Max_Latency = Latency; + } + metal_log(METAL_LOG_DEBUG, "Tile %d, latency %d, max %d\n", + Index, Latency, Max_Latency); + } + } + + /* + * Adjust the latencies to meet the target. Choose max, if it + * is not supplied by the user. + */ + Target = (ConfigPtr->Target_Latency < 0) ? Max_Latency : + ConfigPtr->Target_Latency; + + if (Target < Max_Latency) { + /* Cannot correct for -ve latencies, so default to aligning */ + Target = Max_Latency; + metal_log(METAL_LOG_ERROR, "Error : %s alignment target latency of %d < minimum possible %d\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", Target, Max_Latency); + Status |= XRFDC_MTS_TARGET_LOW; + } + + for (Index = XRFDC_TILE_ID0; Index < XRFDC_TILE_ID4; Index++) { + if (((1U << Index) & ConfigPtr->Tiles) != 0U) { + Delta = Target - ConfigPtr->Latency[Index]; + if (Delta < 0) { + Delta = 0; + } + I_Part = Delta / Factor; + F_Part = Delta % Factor; + Offset = I_Part; + if (F_Part > (int)(Factor / 2U)) { + Offset++; + } + metal_log(METAL_LOG_DEBUG, + "Target %d, Tile %d, delta %d, i/f_part %d/%d, offset %d\n", + Target, Index, Delta, I_Part, F_Part, Offset * Factor); + + /* check for excessive delay correction values */ + if (Offset > (int)XRFDC_MTS_DELAY_MAX) { + Offset = (int)XRFDC_MTS_DELAY_MAX; + metal_log(METAL_LOG_ERROR, + "Alignment correction delay %d" + " required exceeds maximum for %s Tile %d\n", + Offset, (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", + XRFDC_MTS_DELAY_MAX, Index); + Status |= XRFDC_MTS_DELAY_OVER; + } + + /* Adjust the latency, write the same value to each FIFO */ + BaseAddr = XRFDC_DRP_BASE(Type, Index) - 0x2000; + for (Fifo = XRFDC_BLK_ID0; Fifo < XRFDC_BLK_ID4; Fifo++) { + RegAddr = XRFDC_MTS_DELAY_CTRL + (Fifo << 2); + RegData = XRFdc_ReadReg(InstancePtr, BaseAddr, RegAddr); + RegData = XRFDC_MTS_RMW(RegData, XRFDC_MTS_DELAY_VAL_M, + Offset); + XRFdc_WriteReg(InstancePtr, BaseAddr, RegAddr, RegData); + } + + /* Report the total latency for this tile */ + ConfigPtr->Latency[Index] = ConfigPtr->Latency[Index] + (Offset * Factor); + ConfigPtr->Offset[Index] = Offset; + + /* Set the Final SysRef Capture Enable state */ + XRFdc_MTS_Sysref_Ctrl(InstancePtr, Type, Index, 0, ConfigPtr->SysRef_Enable, 0); + } + } + + return Status; +} + +/*****************************************************************************/ +/** +* +* This API is used to enable/disable the sysref. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param DACSyncConfigPtr is pointer to DAC Multi-Tile Sync config structure. +* @param ADCSyncConfigPtr is pointer to ADC Multi-Tile Sync config structure. +* @param SysRefEnable valid values are 0(disable) and 1(enable). +* +* @return +* - XRFDC_MTS_OK if successful. +* +* @note None +* +******************************************************************************/ +u32 XRFdc_MTS_Sysref_Config(XRFdc *InstancePtr, + XRFdc_MultiConverter_Sync_Config *DACSyncConfigPtr, + XRFdc_MultiConverter_Sync_Config *ADCSyncConfigPtr, u32 SysRefEnable) +{ + u32 Tile; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(DACSyncConfigPtr != NULL); + Xil_AssertNonvoid(ADCSyncConfigPtr != NULL); + + /* Enable/disable SysRef Capture on all DACs participating in MTS */ + for (Tile = XRFDC_TILE_ID0; Tile < XRFDC_TILE_ID4; Tile++) { + if (((1U << Tile) & DACSyncConfigPtr->Tiles) != 0U) { + XRFdc_MTS_Sysref_Ctrl(InstancePtr, + XRFDC_DAC_TILE, Tile, 0, SysRefEnable, 0); + } + } + + /* Enable/Disable SysRef Capture on all ADCs participating in MTS */ + for (Tile = XRFDC_TILE_ID0; Tile < XRFDC_TILE_ID4; Tile++) { + if (((1U << Tile) & ADCSyncConfigPtr->Tiles) != 0U) { + XRFdc_MTS_Sysref_Ctrl(InstancePtr, + XRFDC_ADC_TILE, Tile, 0, SysRefEnable, 0); + } + } + + /* Enable/Disable SysRef TRX */ + XRFdc_MTS_Sysref_TRx(InstancePtr, SysRefEnable); + + return XRFDC_MTS_OK; +} + +/*****************************************************************************/ +/** +* +* This API Initializes the multi-tile sync config structures. +* Optionally allows target codes to be provided for the Pll/T1 +* analog sysref capture +* +* @param ConfigPtr pointer to Multi-tile sync config structure. +* @param PLL_CodesPtr pointer to PLL analog sysref capture. +* @param T1_CodesPtr pointer to T1 analog sysref capture. +* +* @return None +* +* @note None +* +******************************************************************************/ +void XRFdc_MultiConverter_Init(XRFdc_MultiConverter_Sync_Config *ConfigPtr, + int *PLL_CodesPtr, int *T1_CodesPtr) +{ + u32 Index; + + Xil_AssertVoid(ConfigPtr != NULL); + + ConfigPtr->RefTile = 0U; + ConfigPtr->DTC_Set_PLL.Scan_Mode = (PLL_CodesPtr == NULL) ? + XRFDC_MTS_SCAN_INIT : XRFDC_MTS_SCAN_RELOAD; + ConfigPtr->DTC_Set_T1.Scan_Mode = (T1_CodesPtr == NULL) ? + XRFDC_MTS_SCAN_INIT : XRFDC_MTS_SCAN_RELOAD; + ConfigPtr->DTC_Set_PLL.IsPLL = 1U; + ConfigPtr->DTC_Set_T1.IsPLL = 0U; + ConfigPtr->Target_Latency = -1; + ConfigPtr->Marker_Delay = 15; + ConfigPtr->SysRef_Enable = 1; /* By default enable Sysref capture after MTS */ + + /* Initialize variables per tile */ + for (Index = XRFDC_TILE_ID0; Index < XRFDC_TILE_ID4; Index++) { + if (PLL_CodesPtr != NULL) { + ConfigPtr->DTC_Set_PLL.Target[Index] = PLL_CodesPtr[Index]; + } else { + ConfigPtr->DTC_Set_PLL.Target[Index] = 0; + } + if (T1_CodesPtr != NULL) { + ConfigPtr->DTC_Set_T1.Target[Index] = T1_CodesPtr[Index]; + } else { + ConfigPtr->DTC_Set_T1.Target[Index] = 0; + } + + ConfigPtr->DTC_Set_PLL.DTC_Code[Index] = -1; + ConfigPtr->DTC_Set_T1.DTC_Code[Index] = -1; + } + +} + +/*****************************************************************************/ +/** +* +* This is the top level API which will be used for Multi-tile +* Synchronization. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC +* @param ConfigPtr Multi-tile sync config structure. +* +* @return +* - XRFDC_MTS_OK if successful. +* - XRFDC_MTS_TIMEOUT if timeout occurs. +* - XRFDC_MTS_MARKER_RUN +* - XRFDC_MTS_MARKER_MISM +* - XRFDC_MTS_NOT_SUPPORTED if MTS is not supported. +* +* @note None +* +******************************************************************************/ +u32 XRFdc_MultiConverter_Sync(XRFdc *InstancePtr, u32 Type, + XRFdc_MultiConverter_Sync_Config *ConfigPtr) +{ + u32 Status; + u32 Index; + u32 RegData; + XRFdc_IPStatus IPStatus = {0}; + XRFdc_MTS_Marker Markers = {0U}; + u32 BaseAddr; + u32 TileState; + u32 BlockStatus; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(ConfigPtr != NULL); + + Status = XRFDC_MTS_OK; + + (void)XRFdc_GetIPStatus(InstancePtr, &IPStatus); + for (Index = XRFDC_TILE_ID0; Index < XRFDC_TILE_ID4; Index++) { + if ((ConfigPtr->Tiles & (1U << Index)) != 0U) { + TileState = (Type == XRFDC_DAC_TILE) ? + IPStatus.DACTileStatus[Index].TileState : + IPStatus.ADCTileStatus[Index].TileState ; + if (TileState != 0xFU) { + metal_log(METAL_LOG_ERROR, + "%s tile %d in Multi-Tile group not started\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", Index); + + Status |= XRFDC_MTS_IP_NOT_READY; + } + BaseAddr = XRFDC_DRP_BASE(Type, Index) - XRFDC_TILE_DRP_OFFSET; + RegData = XRFdc_ReadReg(InstancePtr, BaseAddr, XRFDC_MTS_DLY_ALIGNER); + if (RegData == 0U) { + metal_log(METAL_LOG_ERROR, "%s tile %d is not enabled for MTS, check IP configuration\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", Index); + Status |= XRFDC_MTS_NOT_ENABLED; + } + + BlockStatus = XRFdc_CheckBlockEnabled(InstancePtr, Type, Index, 0x0U); + if (BlockStatus != 0U) { + metal_log(METAL_LOG_ERROR, "%s%d block0 is not enabled, check IP configuration\n", + (Type == XRFDC_ADC_TILE) ? "ADC" : "DAC", Index); + Status |= XRFDC_MTS_NOT_SUPPORTED; + } + } + } + + if (Status != XRFDC_MTS_OK) { + return Status; + } + + /* Disable the FIFOs */ + XRFdc_MTS_FIFOCtrl(InstancePtr, Type, XRFDC_MTS_FIFO_DISABLE, 0); + + /* Enable SysRef Rx */ + XRFdc_MTS_Sysref_TRx(InstancePtr, 1); + + /* Update distribution */ + Status |= XRFdc_MTS_Sysref_Dist(InstancePtr, -1); + + /* Scan DTCs for each tile */ + for (Index = XRFDC_TILE_ID0; Index < XRFDC_TILE_ID4; Index++) { + if ((ConfigPtr->Tiles & (1U << Index)) != 0U) { + /* Run DTC Scan for T1/PLL */ + BaseAddr = XRFDC_DRP_BASE(Type, Index) + XRFDC_HSCOM_ADDR; + RegData = XRFdc_ReadReg16(InstancePtr, BaseAddr, + XRFDC_MTS_CLKSTAT); + if ((RegData & XRFDC_MTS_PLLEN_M) != 0U) { + /* DTC Scan PLL */ + if (Index == 0U) { + metal_log(METAL_LOG_INFO, "\nDTC Scan PLL\n", 0); + } + ConfigPtr->DTC_Set_PLL.RefTile = ConfigPtr->RefTile; + Status |= XRFdc_MTS_Dtc_Scan(InstancePtr, Type, Index, + &ConfigPtr->DTC_Set_PLL); + } + } + } + + /* Scan DTCs for each tile T1 */ + metal_log(METAL_LOG_INFO, "\nDTC Scan T1\n", 0); + for (Index = XRFDC_TILE_ID0; Index < XRFDC_TILE_ID4; Index++) { + if ((ConfigPtr->Tiles & (1U << Index)) != 0U) { + ConfigPtr->DTC_Set_T1 .RefTile = ConfigPtr->RefTile; + Status |= XRFdc_MTS_Dtc_Scan(InstancePtr, Type, Index, + &ConfigPtr->DTC_Set_T1); + } + } + + /* Enable FIFOs */ + XRFdc_MTS_FIFOCtrl(InstancePtr, Type, XRFDC_MTS_FIFO_ENABLE, + ConfigPtr->Tiles); + + /* Measure latency */ + Status |= XRFdc_MTS_GetMarker(InstancePtr, Type, ConfigPtr->Tiles, + &Markers, ConfigPtr->Marker_Delay); + + /* Calculate latency difference and adjust for it */ + Status |= XRFdc_MTS_Latency(InstancePtr, Type, ConfigPtr, &Markers); + + return Status; +} +/*****************************************************************************/ +/** +* +* This is the top level API which will be used to check if Multi-tile +* is enabled. +* +* +* @param InstancePtr is a pointer to the XRfdc instance. +* @param Type is ADC or DAC. 0 for ADC and 1 for DAC. +* @param Tile_Id indicates Tile number (0-3). +* @param EnablePtr to be filled with the enable state. +* +* @return +* - XRFDC_SUCCESS if successful. +* - XRFDC_SUCCESS if error occurs. +* +* @note None +* +******************************************************************************/ +u32 XRFdc_GetMTSEnable(XRFdc *InstancePtr, u32 Type,u32 Tile_Id, u32 *EnablePtr) +{ + u32 RegData; + u32 BaseAddr; + u32 Status; + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(EnablePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XRFDC_COMPONENT_IS_READY); + + Status = XRFdc_CheckTileEnabled(InstancePtr, Type, Tile_Id); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, + "\n Requested Tile not " + "available in %s\r\n", + __func__); + goto RETURN_PATH; + } + + BaseAddr = XRFDC_DRP_BASE(Type, Tile_Id) - XRFDC_TILE_DRP_OFFSET; + RegData = XRFdc_ReadReg(InstancePtr, BaseAddr, XRFDC_MTS_DLY_ALIGNER); + if (RegData == 0) { + *EnablePtr = 0; + } else { + *EnablePtr = 1; + } + Status = XRFDC_SUCCESS; +RETURN_PATH: + return Status; +} +/** @} */ diff --git a/mpm/lib/rfdc/xrfdc_sinit.c b/mpm/lib/rfdc/xrfdc_sinit.c new file mode 100644 index 000000000..0fb907859 --- /dev/null +++ b/mpm/lib/rfdc/xrfdc_sinit.c @@ -0,0 +1,272 @@ +/****************************************************************************** +* +* Copyright (C) 2017-2019 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xrfdc_sinit.c +* @addtogroup rfdc_v6_0 +* @{ +* +* The implementation of the XRFdc component's static initialization +* functionality. +* +* <pre> +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.0 sk 05/16/17 Initial release +* 5.0 mus 08/17/18 Updated XRFdc_LookupConfig to make use of device +* tree instead of xrfdc_g.c, to obtain +* XRFdc_Config for provided device id.It is being +* achieved through "param-list" property in RFDC +* device node, it will be having 1:1 mapping with +* the XRFdc_Config structure. Said changes +* have been done, to remove the xparameters.h +* dependency from RFDC Linux user space driver. +* +* </pre> +* +******************************************************************************/ + +/***************************** Include Files *********************************/ +#define METAL_INTERNAL +#include "mpm/rfdc/xrfdc.h" +#ifdef __BAREMETAL__ +#include "xparameters.h" +#else +#include <dirent.h> +#include <arpa/inet.h> +#endif +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ +#ifdef __BAREMETAL__ +extern XRFdc_Config XRFdc_ConfigTable[]; +#else +static XRFdc_Config *XRFdc_ConfigTablePtr=NULL; +#endif + +#ifndef __BAREMETAL__ +/*****************************************************************************/ +/** +* +* Compare two strings in the reversed order.This function compares only +* the last "Count" number of characters of Str1Ptr and Str2Ptr. +* +* @param Str1Ptr is base address of first string +* @param Str2Ptr is base address of second string +* @param Count is number of last characters to be compared between +* Str1Ptr and Str2Ptr +* +* @return +* 0 if last "Count" number of bytes matches between Str1Ptr and +* Str2Ptr, else differnce in unmatched character. +* +*@note None. +* +******************************************************************************/ +static s32 XRFdc_Strrncmp(const char *Str1Ptr, const char *Str2Ptr, size_t Count) +{ + u16 Len1 = strlen(Str1Ptr); + u16 Len2 = strlen(Str2Ptr); + u8 Diff; + + for (; Len1 && Len2; Len1--, Len2--) { + if ((Diff = Str1Ptr[Len1 - 1] - Str2Ptr[Len2 - 1]) != 0) { + return Diff; + } + if (--Count == 0) { + return 0; + } + } + + return (Len1 - Len2); +} + +/*****************************************************************************/ +/** +* +* Traverse "/sys/bus/platform/device" directory, to find RFDC device entry, +* corresponding to provided device id. If device entry corresponding to said +* device id is found, store it in output buffer DevNamePtr. +* +* @param DevNamePtr is base address of char array, where device name +* will be stored +* @param DevId contains the ID of the device to look up the +* RFDC device name entry in "/sys/bus/platform/device" +* +* @return + * - XRFDC_SUCCESS if successful. + * - XRFDC_FAILURE if device entry not found for given device id. + * + *@note None. +* +******************************************************************************/ +s32 XRFdc_GetDeviceNameByDeviceId(char *DevNamePtr, u16 DevId) +{ + s32 Status = XRFDC_FAILURE; + u32 Data = 0; + char CompatibleString[NAME_MAX]; + struct metal_device *DevicePtr; + DIR *DirPtr; + struct dirent *DirentPtr; + char Len = strlen(XRFDC_COMPATIBLE_STRING); + char SignLen = strlen(XRFDC_SIGNATURE); + + DirPtr = opendir(XRFDC_PLATFORM_DEVICE_DIR); + if (DirPtr) { + while ((DirentPtr = readdir(DirPtr)) != NULL) { + if (XRFdc_Strrncmp(DirentPtr->d_name, + XRFDC_SIGNATURE, SignLen) == 0) { + Status = metal_device_open("platform",DirentPtr->d_name, + &DevicePtr); + if (Status) { + metal_log(METAL_LOG_ERROR, + "\n Failed to open device %s", DirentPtr->d_name); + continue; + } + Status = metal_linux_get_device_property(DevicePtr, + XRFDC_COMPATIBLE_PROPERTY, CompatibleString , + Len); + if (Status < 0) { + metal_log(METAL_LOG_ERROR, + "\n Failed to read device tree property"); + } else if (strncmp(CompatibleString, \ + XRFDC_COMPATIBLE_STRING, Len) == 0) { + Status = metal_linux_get_device_property(DevicePtr, + XRFDC_CONFIG_DATA_PROPERTY, + &Data, XRFDC_DEVICE_ID_SIZE); + if (Status < 0) { + metal_log(METAL_LOG_ERROR, + "\n Failed to read device tree property"); + } else if ( Data == DevId ) { + strcpy(DevNamePtr, DirentPtr->d_name); + Status = XRFDC_SUCCESS; + metal_device_close(DevicePtr); + break; + } + } + metal_device_close(DevicePtr); + } + } + closedir(DirPtr); + } + return Status; +} +#endif +/*****************************************************************************/ +/** +* +* Looks up the device configuration based on the unique device ID. A table +* contains the configuration info for each device in the system. +* +* @param DeviceId contains the ID of the device to look up the +* configuration for. +* +* @return +* +* A pointer to the configuration found or NULL if the specified device ID was +* not found. See xrfdc.h for the definition of XRFdc_Config. +* +* @note None. +* +******************************************************************************/ +XRFdc_Config *XRFdc_LookupConfig(u16 DeviceId) +{ + XRFdc_Config *CfgPtr = NULL; +#ifndef __BAREMETAL__ + s32 Status=0; + u32 NumInstances; + struct metal_device *Deviceptr; + char DeviceName[NAME_MAX]; + + Status = XRFdc_GetDeviceNameByDeviceId(DeviceName, DeviceId); + if (Status != XRFDC_SUCCESS) { + metal_log(METAL_LOG_ERROR, "\n Invalid device id %d", DeviceId); + goto RETURN_PATH2; + } + + Status = metal_device_open(XRFDC_BUS_NAME, DeviceName, &Deviceptr); + if (Status) { + metal_log(METAL_LOG_ERROR, "\n Failed to open device %s", DeviceName); + goto RETURN_PATH2; + } + + if (XRFdc_ConfigTablePtr == NULL) { + Status = metal_linux_get_device_property(Deviceptr, + XRFDC_NUM_INSTANCES_PROPERTY, + &NumInstances, XRFDC_NUM_INST_SIZE); + if (Status < 0) { + metal_log(METAL_LOG_ERROR, + "\n Failed to read device tree property %s", + XRFDC_NUM_INSTANCES_PROPERTY); + goto RETURN_PATH1; + } + XRFdc_ConfigTablePtr = (XRFdc_Config*) malloc(ntohl(NumInstances) * \ + XRFDC_CONFIG_DATA_SIZE); + if (XRFdc_ConfigTablePtr == NULL) { + metal_log(METAL_LOG_ERROR, + "\n Failed to allocate memory for XRFdc_ConfigTablePtr"); + goto RETURN_PATH1; + } + } + Status = metal_linux_get_device_property(Deviceptr, + XRFDC_CONFIG_DATA_PROPERTY, + &XRFdc_ConfigTablePtr[DeviceId], + XRFDC_CONFIG_DATA_SIZE); + if (Status == XRFDC_SUCCESS) { + CfgPtr = &XRFdc_ConfigTablePtr[DeviceId]; + } else { + metal_log(METAL_LOG_ERROR, + "\n Failed to read device tree property %s", + XRFDC_CONFIG_DATA_PROPERTY); + + } +RETURN_PATH1: + metal_device_close(Deviceptr); +RETURN_PATH2: +#else + u32 Index; + + for (Index = 0U; Index < (u32)XPAR_XRFDC_NUM_INSTANCES; Index++) { + if (XRFdc_ConfigTable[Index].DeviceId == DeviceId) { + CfgPtr = &XRFdc_ConfigTable[Index]; + break; + } + } +#endif + return (XRFdc_Config *)CfgPtr; +} +/** @} */ diff --git a/mpm/python/CMakeLists.txt b/mpm/python/CMakeLists.txt index 1900c4004..ba4bf075b 100644 --- a/mpm/python/CMakeLists.txt +++ b/mpm/python/CMakeLists.txt @@ -55,6 +55,8 @@ elseif(MPM_DEVICE STREQUAL "e320") add_library(pyusrp_periphs SHARED pyusrp_periphs/e320/pyusrp_periphs.cpp) elseif(MPM_DEVICE STREQUAL "e31x") add_library(pyusrp_periphs SHARED pyusrp_periphs/e31x/pyusrp_periphs.cpp) +elseif(MPM_DEVICE STREQUAL "x4xx") + add_library(pyusrp_periphs SHARED pyusrp_periphs/x4xx/pyusrp_periphs.cpp) endif(MPM_DEVICE STREQUAL "n3xx") if(NOT ENABLE_SIM) @@ -114,6 +116,11 @@ elseif (ENABLE_E320) e320_bist DESTINATION ${RUNTIME_DIR} ) +elseif (ENABLE_X400) + install(PROGRAMS + x4xx_bist + DESTINATION ${RUNTIME_DIR} + ) endif (ENABLE_MYKONOS) if (HAVE_MPM_TEST_PREREQS) diff --git a/mpm/python/pyusrp_periphs/x4xx/pyusrp_periphs.cpp b/mpm/python/pyusrp_periphs/x4xx/pyusrp_periphs.cpp new file mode 100644 index 000000000..31b1ed6c4 --- /dev/null +++ b/mpm/python/pyusrp_periphs/x4xx/pyusrp_periphs.cpp @@ -0,0 +1,27 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <pybind11/pybind11.h> +namespace py = pybind11; +#define LIBMPM_PYTHON + +// Allow boost::shared_ptr<T> to be a holder class of an object (PyBind11 +// supports std::shared_ptr and std::unique_ptr out of the box) +#include <boost/shared_ptr.hpp> +PYBIND11_DECLARE_HOLDER_TYPE(T, boost::shared_ptr<T>); + +#include <mpm/i2c/i2c_python.hpp> +#include <mpm/rfdc/rfdc_ctrl.hpp> +#include <mpm/spi/spi_python.hpp> +#include <mpm/types/types_python.hpp> + +PYBIND11_MODULE(libpyusrp_periphs, m) +{ + export_types(m); + export_spi(m); + export_i2c(m); + export_rfdc(m); +} diff --git a/mpm/python/tests/components_tests.py b/mpm/python/tests/components_tests.py new file mode 100644 index 000000000..120b89121 --- /dev/null +++ b/mpm/python/tests/components_tests.py @@ -0,0 +1,114 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Tests the components classes (currently ZynqComponents) +""" + +from usrp_mpm.components import ZynqComponents +from base_tests import TestBase + +import copy +import os.path +import tempfile +import unittest + +class TestZynqComponents(TestBase): + """ + Test functions of the ZynqComponents class + """ + + _testcase_input = '// mpm_version foo_current_version 1.10\n' \ + '// mpm_version foo_oldest_compatible_version 1.5\n' \ + '// mpm_version bar_current_version 1.2\n' \ + '// mpm_version bar_oldest_compatible_version 1.0\n' \ + '// mpm_version baz_current_version 1.2.3\n' \ + '// mpm_version baz_oldest_compatible_version 1.0.0\n' \ + '// mpm_version zack 2.0\n' \ + '// mpm_version zack_oldest_compatible_version 1.0\n' \ + '// mpm_other_tag noname_current_version 1.2.3\n' \ + '// other comment\n' + + _testcase_result = { + 'bar': {'current': (1, 2), 'oldest': (1,0)}, + 'baz': {'current': (1, 2, 3), 'oldest': (1,0,0)}, + 'foo': {'current': (1, 10), 'oldest': (1, 5)}, + 'zack': {'current': (2, 0), 'oldest': (1,0)}, + } + + def _write_dts_file_from_test_cases(self, content): + """ Write content to a temporary .dts file """ + f = tempfile.NamedTemporaryFile(mode="w+", suffix=".dts") + expected = {} + f.write(content) + f.flush() + return f + + def test_parse_dts_version_info_from_file(self): + """ Test function ZynqComponents._parse_dts_version_info_from_file """ + f = self._write_dts_file_from_test_cases(self._testcase_input) + expected = self._testcase_result + result = ZynqComponents._parse_dts_version_info_from_file(f.name) + self.assertEqual(result, expected) + + def test_verify_compatibility(self): + """ Test function ZynqComponents._verify_compatibility """ + class _log_dummy(): + def _dummy(self, *args): + pass + trace = _dummy + info = _dummy + warning = _dummy + error = _dummy + + f = self._write_dts_file_from_test_cases(self._testcase_input) + compatibility = self._testcase_result + for version_type in ['current', 'oldest']: + for case in ['normal', 'smaller_mpm_minor', 'bigger_mpm_minor', + 'smaller_mpm_major', 'bigger_mpm_major', 'component_missing', + 'additional_component']: + compatibility_testcase = copy.deepcopy(compatibility) + foo_major, foo_minor = compatibility['foo'][version_type] + if case == 'normal': + compatibility_testcase['foo'][version_type] = (foo_major, foo_minor) + error_expected = None + elif case == 'smaller_mpm_minor': + compatibility_testcase['foo'][version_type] = (foo_major, foo_minor-1) + error_expected = None + elif case == 'bigger_mpm_minor': + compatibility_testcase['foo'][version_type] = (foo_major, foo_minor+1) + error_expected = None + elif case == 'smaller_mpm_major': + compatibility_testcase['foo'][version_type] = (foo_major-1, foo_minor) + if version_type == 'oldest': + error_expected = None + else: + error_expected = RuntimeError() + elif case == 'bigger_mpm_major': + compatibility_testcase['foo'][version_type] = (foo_major+1, foo_minor) + if version_type == 'oldest': + error_expected = RuntimeError() + else: + error_expected = None + elif case == 'component_missing': + del compatibility_testcase['foo'] + error_expected = None + elif case == 'additional_component': + compatibility_testcase['newcomp'] = {version_type: (2, 10)} + error_expected = None + update_dict = { + 'compatibility': compatibility_testcase, + 'check_dts_for_compatibility': True, + } + filebasename, _ = os.path.splitext(f.name) + try: + self._zynqcomponents = ZynqComponents() + self._zynqcomponents.log = _log_dummy() + self._zynqcomponents._verify_compatibility(filebasename, update_dict) + error = None + except RuntimeError as r: + error = r + self.assertEqual(error.__class__, error_expected.__class__, + f"Unexpected result for test case {case} (version type: {version_type})") diff --git a/mpm/python/tests/run_unit_tests.py b/mpm/python/tests/run_unit_tests.py index c563804ae..a26fa1d22 100755 --- a/mpm/python/tests/run_unit_tests.py +++ b/mpm/python/tests/run_unit_tests.py @@ -13,6 +13,7 @@ import argparse from sys_utils_tests import TestNet from mpm_utils_tests import TestMpmUtils from eeprom_tests import TestEeprom +from usrp_mpm import __simulated__ import importlib.util if importlib.util.find_spec("xmlrunner"): @@ -25,8 +26,15 @@ TESTS = { TestEeprom, }, 'n3xx': set(), + 'x4xx': set() } +if not __simulated__: + from components_tests import TestZynqComponents + TESTS['x4xx'].update({ + TestZynqComponents + }) + def parse_args(): """Parse arguments when running this as a script""" parser_help = 'Run MPM Python unittests' diff --git a/mpm/python/tests/sys_utils_tests.py b/mpm/python/tests/sys_utils_tests.py index 50a10e1a1..c189257c2 100755 --- a/mpm/python/tests/sys_utils_tests.py +++ b/mpm/python/tests/sys_utils_tests.py @@ -62,6 +62,11 @@ class TestNet(TestBase): """ if self.device_name == 'n3xx': possible_ifaces = ['eth0', 'sfp0', 'sfp1'] + elif self.device_name == 'x4xx': + # x4xx devices have an internal network interface + # TODO: change this when sfp0 is enabled + # possible_ifaces = ['eth0', 'sfp0', 'int0'] + possible_ifaces = ['eth0', 'int0'] else: possible_ifaces = ['eth0', 'sfp0'] diff --git a/mpm/python/tests/test_utilities.py b/mpm/python/tests/test_utilities.py index 198eda5e8..942ad956a 100755 --- a/mpm/python/tests/test_utilities.py +++ b/mpm/python/tests/test_utilities.py @@ -90,6 +90,7 @@ class MockRegsIface(object): self.map = register_map self.recent_vals = {} self.next_vals = {} + self.recent_addrs = [] def peek32(self, addr): """ @@ -110,12 +111,32 @@ class MockRegsIface(object): """ self.map.set_reg(addr, value) + self.recent_addrs.append(addr) + # Store written value in a list if addr in self.recent_vals: self.recent_vals[addr].append(value) else: self.recent_vals[addr] = [value] + def peek16(self, addr): + """ + Pass the request to the 32 bit version + """ + return self.peek32(addr) & 0xFFFF + + def poke16(self, addr, value): + """ + Pass the request to the 32 bit version + """ + self.poke32(addr, value) + + def get_recent_addrs(self): + return self.recent_addrs + + def clear_recent_addrs(self): + self.recent_addrs = [] + def get_recent_vals(self, addr): """ Returns the past values written to a given address. @@ -123,6 +144,13 @@ class MockRegsIface(object): """ return self.recent_vals.get(addr, []) + def clear_recent_vals(self, addr): + """ + Clears the past values written to a given address. + Useful for validating HW interaction + """ + self.recent_vals[addr] = [] + def set_next_vals(self, addr, vals): """ Sets a list of the next values to be read from the diff --git a/mpm/python/usrp_mpm/bist.py b/mpm/python/usrp_mpm/bist.py index bd0dbb8d3..6678b132d 100644 --- a/mpm/python/usrp_mpm/bist.py +++ b/mpm/python/usrp_mpm/bist.py @@ -18,6 +18,7 @@ from datetime import datetime import argparse import subprocess from six import iteritems +from usrp_mpm.sys_utils import ectool ############################################################################## # Aurora/SFP BIST code @@ -220,6 +221,7 @@ def get_product_id_from_eeprom(valid_ids, cmd='eeprom-id'): """Return the mboard product ID Returns something like n300, n310, e320... + the eeprom parameter is needed if there are several eeprom within the system """ output = subprocess.check_output( [cmd], @@ -268,13 +270,24 @@ def gpio_set_all(gpio_bank, value, gpio_size, ddr_mask): ############################################################################## # Common tests ############################################################################## -def test_ddr3_with_usrp_probe(): +def test_ddr3_with_usrp_probe(extra_args=None): """ Run uhd_usrp_probe and scrape the output to see if the DRAM FIFO block is reporting a good throughput. This is a bit of a roundabout way of testing the DDR3, but it uses existing software and also tests the RFNoC pathways. """ - ddr3_bist_executor = 'uhd_usrp_probe --args addr=127.0.0.1' + dflt_args = {'addr':'127.0.0.1', 'rfnoc_num_blocks':1} + extra_args = extra_args or {} + # merge args dicts, extra_args overrides dflt_args if keys exists in both dicts + args = {**dflt_args, **extra_args} + args_str = ",".join( + ['{k}={v}'.format(k=k, v=v) for k, v in iteritems(args)]) + cmd = [ + 'uhd_usrp_probe', + '--args', + '{e}'.format(e=args_str) + ] + ddr3_bist_executor = ' '.join(cmd) try: output = subprocess.check_output( ddr3_bist_executor, @@ -335,15 +348,19 @@ def get_ref_clock_prop(clock_source, time_source, extra_args=None): - <sensor-name>: - locked: Boolean lock status """ + dflt_args = {'addr':'127.0.0.1'} extra_args = extra_args or {} + # merge args dicts, extra_args overrides dflt_args if keys exists in both dicts + args = {**dflt_args, **extra_args} result = {} - extra_args_str = ",".join( - ['{k}={v}'.format(k=k, v=v) for k, v in iteritems(extra_args)]) + + args_str = ",".join( + ['{k}={v}'.format(k=k, v=v) for k, v in iteritems(args)]) cmd = [ 'uhd_usrp_probe', '--args', - 'addr=127.0.0.1,clock_source={c},time_source={t},{e}'.format( - c=clock_source, t=time_source, e=extra_args_str), + 'clock_source={c},time_source={t},{e}'.format( + c=clock_source, t=time_source, e=args_str), '--sensor' ] sensor_path = '/mboards/0/sensors/ref_locked' @@ -382,6 +399,33 @@ def get_temp_sensor_value(temp_sensor_map): and device.attributes.get('temp') is not None } + +def get_iio_temp_sensor_values(): + """ + Read all devices in the IIO subsystem that can report a temperature and + returns dictionary containing {name: temperature}, where name is the + temperature device name and the temperature is a value in mC. + """ + import pyudev + context = pyudev.Context() + iio_devs = context.list_devices(subsystem='iio') + + def is_temp_dev(dev): + return 'in_temp_raw' in dev.attributes.available_attributes + + def get_temp(dev): + raw = float(dev.attributes.get('in_temp_raw').decode('ascii')) + offset = float(dev.attributes.get('in_temp_offset').decode('ascii')) + scale = float(dev.attributes.get('in_temp_scale').decode('ascii')) + return int(scale * (raw + offset)) + + def get_name(dev): + return dev.attributes.get('name').decode('ascii') + + temp_devs = [dev for dev in iio_devs if is_temp_dev(dev)] + return {get_name(dev): get_temp(dev) for dev in temp_devs} + + def get_fan_values(): """ Return a dict of fan name -> fan speed key/values. @@ -395,6 +439,14 @@ def get_fan_values(): and device.attributes.get('cur_state') is not None } +def get_ectool_fan_values(): + try: + return ectool.get_fan_rpm() + except RuntimeError as ex: + return { + 'error_msg': "{}".format(str(ex)) + } + def get_link_up(if_name): """ Return a dictionary {if_name: IFLA_OPERSTATE} diff --git a/mpm/python/usrp_mpm/chips/CMakeLists.txt b/mpm/python/usrp_mpm/chips/CMakeLists.txt index 5c5670b14..692e4f11d 100644 --- a/mpm/python/usrp_mpm/chips/CMakeLists.txt +++ b/mpm/python/usrp_mpm/chips/CMakeLists.txt @@ -7,12 +7,14 @@ set(USRP_MPM_FILES ${USRP_MPM_FILES}) set(USRP_MPM_CHIP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py + ${CMAKE_CURRENT_SOURCE_DIR}/adf400x.py + ${CMAKE_CURRENT_SOURCE_DIR}/ds125df410.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk04828.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk04832.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk03328.py - ${CMAKE_CURRENT_SOURCE_DIR}/adf400x.py - ${CMAKE_CURRENT_SOURCE_DIR}/ds125df410.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk05318.py + ${CMAKE_CURRENT_SOURCE_DIR}/lmx2572.py + ${CMAKE_CURRENT_SOURCE_DIR}/max10_cpld_flash_ctrl.py ) list(APPEND USRP_MPM_FILES ${USRP_MPM_CHIP_FILES}) add_subdirectory(ic_reg_maps) diff --git a/mpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt b/mpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt index 631f30264..1a49daef6 100755 --- a/mpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt +++ b/mpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt @@ -34,6 +34,14 @@ if(ENABLE_REGMAPS) ${UHD_HOST_ROOT}/lib/ic_reg_maps/gen_lmk04816_regs.py ${CMAKE_CURRENT_BINARY_DIR}/lmk04816_regs.py ) + REG_MAPS_GEN_SOURCE( + ${UHD_HOST_ROOT}/lib/ic_reg_maps/gen_lmx2572_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/lmx2572_regs.py + ) + REG_MAPS_GEN_SOURCE( + ${UHD_HOST_ROOT}/lib/ic_reg_maps/gen_zbx_cpld_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/zbx_cpld_regs.py + ) # add an ic_reg_maps target which can be referenced outside of this subdirectory add_custom_target(ic_reg_maps DEPENDS ${IC_REG_MAPS}) diff --git a/mpm/python/usrp_mpm/chips/lmx2572.py b/mpm/python/usrp_mpm/chips/lmx2572.py new file mode 100644 index 000000000..e480dd53b --- /dev/null +++ b/mpm/python/usrp_mpm/chips/lmx2572.py @@ -0,0 +1,338 @@ +# +# Copyright 2019-2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +LMX2572 parent driver class +""" + +import math +from builtins import object +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.chips.ic_reg_maps import lmx2572_regs_t + +NUMBER_OF_LMX2572_REGISTERS = 126 + +class LMX2572(object): + """ + Generic driver class for LMX2572 access. + """ + + READ_ONLY_REGISTERS = [107, 108, 109, 110, 111, 112, 113] + + def __init__(self, regs_iface, parent_log = None): + self.log = parent_log + + self.regs_iface = regs_iface + assert hasattr(self.regs_iface, 'peek16') + assert hasattr(self.regs_iface, 'poke16') + self._poke16 = regs_iface.poke16 + self._peek16 = regs_iface.peek16 + + self._lmx2572_regs = lmx2572_regs_t() + + self._need_recalculation = True + self._enabled = False + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, enable): + self._set_chip_enable(bool(enable)) + self._enabled = bool(enable) + + def reset(self): + """ + Performs a reset of the LMX2572 by using the software reset register. + """ + self._lmx2572_regs = lmx2572_regs_t() + self._lmx2572_regs.reset = lmx2572_regs_t.reset_t.RESET_RESET + self._poke16(0, self._lmx2572_regs.get_reg(0)) + self._lmx2572_regs.reset = lmx2572_regs_t.reset_t.RESET_NORMAL_OPERATION + self._set_default_values() + self._power_up_sequence() + + def commit(self): + """ + Calculates the settings when needed and writes the settings to the device + """ + if self._need_recalculation: + self._calculate_settings() + self._need_recalculation = False + self._write_registers_reference_chain() + self._write_registers_frequency_tuning() + + def check_pll_locked(self): + """ + Returns True if the PLL is locked, False otherwise. + """ + # SPI MISO is multiplexed to lock detect and register readback. Reading any + # register when the mux is set to the lock detect will return just the lock detect signal + self._lmx2572_regs.muxout_ld_sel = lmx2572_regs_t.muxout_ld_sel_t.MUXOUT_LD_SEL_LOCK_DETECT + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + # If the PLL is locked we expect to read 0xFFFF from any read, else 0x0000 + value_read = self._peek16(0) + + return value_read == 0xFFFF + + def set_synchronization(self, enable_synchronization): + """ + Enables and disables the phase synchronization + """ + vco_phase_sync = lmx2572_regs_t.vco_phase_sync_en_t.VCO_PHASE_SYNC_EN_PHASE_SYNC_MODE if \ + enable_synchronization else \ + lmx2572_regs_t.vco_phase_sync_en_t.VCO_PHASE_SYNC_EN_NORMAL_OPERATION + self._lmx2572_regs.vco_phase_sync_en = vco_phase_sync + self._need_recalculation = True + + def get_synchronization(self): + """ + Returns the enabled/disabled state of the phase synchronization + """ + return self._lmx2572_regs.vco_phase_sync_en == \ + lmx2572_regs_t.vco_phase_sync_en_t.VCO_PHASE_SYNC_EN_PHASE_SYNC_MODE + + def set_output_enable_all(self, enable_output): + """ + Enables or disables the output on both ports + """ + self._set_output_a_enable(enable_output) + self._set_output_b_enable(enable_output) + + def _set_chip_enable(self, chip_enable): + """ + Enables or disables the LMX2572 using the powerdown register + All other registers are maintained during powerdown + """ + powerdown = lmx2572_regs_t.powerdown_t.POWERDOWN_NORMAL_OPERATION if chip_enable else \ + lmx2572_regs_t.powerdown_t.POWERDOWN_POWER_DOWN + self._lmx2572_regs.powerdown = powerdown + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + def peek16(self, address): + """ + Wraps _peek16 to account for mux_ld_sel + """ + # SPI MISO is multiplexed to lock detect and register readback. Set the mux to register + # readback before trying to read the register. + self._lmx2572_regs.muxout_ld_sel = \ + lmx2572_regs_t.muxout_ld_sel_t.MUXOUT_LD_SEL_REGISTER_READBACK + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + value_read = self._peek16(address) + + self._lmx2572_regs.muxout_ld_sel = lmx2572_regs_t.muxout_ld_sel_t.MUXOUT_LD_SEL_LOCK_DETECT + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + return value_read + + + def _calculate_settings(self): + """ + This function is intended to be called for calculating the register settings, + however it is implementation, not chip specific, so it is defined but not implemented + """ + raise NotImplementedError("This function is meant to be overriden by a child class.") + + def _set_default_values(self): + """ + The register map has all base class defaults. + Subclasses can override this function to have the values populated on reset. + """ + pass + + def _pokes16(self, addr_vals): + """ + Apply a series of pokes. + pokes16((0,1),(0,2)) is the same as calling poke16(0,1), poke16(0,2). + """ + for addr, val in addr_vals: + self._poke16(addr, val) + + def _set_output_a_enable(self, enable_output): + """ + Sets output A (OUTA_PD) + """ + new_value = lmx2572_regs_t.outa_pd_t.OUTA_PD_NORMAL_OPERATION if enable_output \ + else lmx2572_regs_t.outa_pd_t.OUTA_PD_POWER_DOWN + self._lmx2572_regs.outa_pd = new_value + + def _set_output_b_enable(self, enable_output): + """ + Sets output B (OUTB_PD) + """ + new_value = lmx2572_regs_t.outb_pd_t.OUTB_PD_NORMAL_OPERATION if enable_output \ + else lmx2572_regs_t.outb_pd_t.OUTB_PD_POWER_DOWN + self._lmx2572_regs.outb_pd = new_value + + def _set_output_a_power(self, power): + """ + Sets the output A power + """ + self._lmx2572_regs.outa_pwr = power & self._lmx2572_regs.outa_pwr_mask + + def _set_output_b_power(self, power): + """ + Sets the output B power + """ + self._lmx2572_regs.outb_pwr = power & self._lmx2572_regs.outb_pwr_mask + + def _set_fcal_hpfd_adj(self, phase_detector_frequency): + """ + Sets the FCAL_HPFD_ADJ value based on the Fpfd + """ + # These magic number frequency constants are from the data sheet + if phase_detector_frequency <= 37.5e6: + self._lmx2572_regs.fcal_hpfd_adj = 0x0 + elif 37.5e6 < phase_detector_frequency <= 75e6: + self._lmx2572_regs.fcal_hpfd_adj = 0x1 + elif 75e6 < phase_detector_frequency <= 100e6: + self._lmx2572_regs.fcal_hpfd_adj = 0x2 + else: # 100MHz < phase_detector_frequency + self._lmx2572_regs.fcal_hpfd_adj = 0x3 + + def _set_fcal_lpfd_adj(self, phase_detector_frequency): + """ + Sets the FCAL_LPFD_ADJ value based on the Fpfd + """ + # These magic number frequency constants are from the data sheet + if phase_detector_frequency >= 10e6: + self._lmx2572_regs.fcal_lpfd_adj = 0x0 + elif 10e6 > phase_detector_frequency >= 5e6: + self._lmx2572_regs.fcal_lpfd_adj = 0x1 + elif 5e6 > phase_detector_frequency >= 2.5e6: + self._lmx2572_regs.fcal_lpfd_adj = 0x2 + else: # phase_detector_frequency < 2.5MHz + self._lmx2572_regs.fcal_lpfd_adj = 0x3 + + def _set_pll_n(self, n): + """ + Sets the pll_n registers + """ + self._lmx2572_regs.pll_n_upper_3_bits = (n >> 16) & \ + self._lmx2572_regs.pll_n_upper_3_bits_mask + self._lmx2572_regs.pll_n_lower_16_bits = n & self._lmx2572_regs.pll_n_lower_16_bits_mask + + def _set_pll_den(self, den): + """ + Sets the pll_den registers + """ + self._lmx2572_regs.pll_den_upper = (den >> 16) & self._lmx2572_regs.pll_den_upper_mask + self._lmx2572_regs.pll_den_lower = den & self._lmx2572_regs.pll_den_lower_mask + + def _set_mash_seed(self, mash_seed): + """ + Sets the mash seed register + """ + self._lmx2572_regs.mash_seed_upper = (mash_seed >> 16) & \ + self._lmx2572_regs.mash_seed_upper_mask + self._lmx2572_regs.mash_seed_lower = mash_seed & self._lmx2572_regs.mash_seed_lower_mask + + def _set_pll_num(self, num): + """ + Sets the pll_num registers + """ + self._lmx2572_regs.pll_num_upper = (num >> 16) & self._lmx2572_regs.pll_num_upper_mask + self._lmx2572_regs.pll_num_lower = num & self._lmx2572_regs.pll_num_lower_mask + + def _set_mash_rst_count(self, mash_rst_count): + """ + Sets the mash_rst_count registers + """ + self._lmx2572_regs.mash_rst_count_upper = (mash_rst_count >> 16) & \ + self._lmx2572_regs.mash_rst_count_upper_mask + self._lmx2572_regs.mash_rst_count_lower = mash_rst_count & \ + self._lmx2572_regs.mash_rst_count_lower_mask + + def _compute_and_set_mult_hi(self, reference_frequency): + multiplier_output_frequency = (reference_frequency*(int(self._lmx2572_regs.osc_2x.value)\ + +1)*self._lmx2572_regs.mult) / self._lmx2572_regs.pll_r_pre + new_mult_hi = lmx2572_regs_t.mult_hi_t.MULT_HI_GREATER_THAN_100M \ + if self._lmx2572_regs.mult > 1 and multiplier_output_frequency > 100e6 else \ + lmx2572_regs_t.mult_hi_t.MULT_HI_LESS_THAN_EQUAL_TO_100M + self._lmx2572_regs.mult_hi = new_mult_hi + + def _power_up_sequence(self): + """ + Performs the intial register writes for the LMX2572 + """ + for register in reversed(range(NUMBER_OF_LMX2572_REGISTERS)): + if register in LMX2572.READ_ONLY_REGISTERS: + continue + self._poke16(register, self._lmx2572_regs.get_reg(register)) + + def _write_registers_frequency_tuning(self): + """ + This function writes just the registers for frequency tuning + """ + # Write PLL_N to registers R34 and R36 + self._poke16(34, self._lmx2572_regs.get_reg(34)) + self._poke16(36, self._lmx2572_regs.get_reg(36)) + # Write PLL_DEN to registers R38 and R39 + self._poke16(38, self._lmx2572_regs.get_reg(38)) + self._poke16(39, self._lmx2572_regs.get_reg(39)) + # Write PLL_NUM to registers R42 and R43 + self._poke16(42, self._lmx2572_regs.get_reg(42)) + self._poke16(43, self._lmx2572_regs.get_reg(43)) + + # MASH_SEED to registers R40 and R41 + self._poke16(40, self._lmx2572_regs.get_reg(40)) + self._poke16(41, self._lmx2572_regs.get_reg(41)) + + # Write OUTA_PWR to register R44 or OUTB_PWR to register R45 + # Write OUTA_MUX to register R45 and/or OUTB_MUX to register R46 + self._poke16(44, self._lmx2572_regs.get_reg(44)) + self._poke16(45, self._lmx2572_regs.get_reg(45)) + self._poke16(46, self._lmx2572_regs.get_reg(46)) + + # Write CHDIV to register R75 + self._poke16(75, self._lmx2572_regs.get_reg(75)) + + # Write CPG to register R14 + self._poke16(14, self._lmx2572_regs.get_reg(14)) + + # Write PFD_DLY_SEL to register R37 + self._poke16(37, self._lmx2572_regs.get_reg(37)) + + # Write VCO_SEL to register R20 + self._poke16(20, self._lmx2572_regs.get_reg(20)) + + # Write VCO_DACISET_STRT to register R17 + self._poke16(17, self._lmx2572_regs.get_reg(17)) + + # Write VCO_CALCTRL_STRT to register R78 + self._poke16(78, self._lmx2572_regs.get_reg(78)) + + # Write R0 to latch double buffered registers + self._poke16(0, self._lmx2572_regs.get_reg(0)) + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + def _write_registers_reference_chain(self): + """ + This function writes the registers that are used for setting the reference chain + """ + # Write FCAL_HPFD_ADJ to register R0 + # Write FCAL_LPFD_ADJ to register R0 + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + # Write MULT_HI to register R9 + # Write OSC_2X to register R9 + self._poke16(9, self._lmx2572_regs.get_reg(9)) + + # Write MULT to register R10 + self._poke16(10, self._lmx2572_regs.get_reg(10)) + + # Write PLL_R to register R11 + self._poke16(11, self._lmx2572_regs.get_reg(11)) + # Write PLL_R_PRE to register R12 + self._poke16(12, self._lmx2572_regs.get_reg(12)) + + # if Phase SYNC being used: + # Write MASH_RST_COUNT to registers R69 and 70 + if self.get_synchronization(): + self._poke16(70, self._lmx2572_regs.get_reg(70)) + self._poke16(69, self._lmx2572_regs.get_reg(69)) diff --git a/mpm/python/usrp_mpm/chips/max10_cpld_flash_ctrl.py b/mpm/python/usrp_mpm/chips/max10_cpld_flash_ctrl.py new file mode 100644 index 000000000..c1e756124 --- /dev/null +++ b/mpm/python/usrp_mpm/chips/max10_cpld_flash_ctrl.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Update the CPLD image using the on-chip flash on Intel MAX10 devices +""" +import os +import time + +class Max10CpldFlashCtrl(): + """ + Context manager class used to handle CPLD Flash reconfiguration. + Calling "with" using an instance of this class will disable write + protection for the duration of that context. + """ + REVISION_REG = 0x0004 + # Addresses relative to reconfiguration register offset + FLASH_STATUS_REG = 0x0000 + FLASH_CONTROL_REG = 0x0004 + FLASH_ADDR_REG = 0x0008 + FLASH_WRITE_DATA_REG = 0x000C + FLASH_READ_DATA_REG = 0x0010 + FLASH_CFM0_START_ADDR_REG = 0x0014 + FLASH_CFM0_END_ADDR_REG = 0x0018 + + # Masks for FLASH_STATUS_REG + FLASH_MEM_INIT_ENABLED_MASK = 0x10000 + + def __init__(self, logger, regs, reconfig_regs_offset, cpld_min_revision): + if logger == None: + logger = get_logger('update_cpld') + self.log = logger + self.regs = regs + self.reconfig_regs_offset = reconfig_regs_offset + self.cpld_min_revision = cpld_min_revision + + def peek32(self, addr): + return self.regs.peek32(addr + self.reconfig_regs_offset) + + def poke32(self, addr, val): + self.regs.poke32(addr + self.reconfig_regs_offset, val) + + def __enter__(self): + self.enabled_write_protection(enable=False) + + def __exit__(self, exc_type, exc_value, traceback): + self.enabled_write_protection(enable=True) + + def enabled_write_protection(self, enable=True): + if enable: + self.poke32(self.FLASH_CONTROL_REG, 1 << 0) # FLASH_ENABLE_WP_STB + else: + self.poke32(self.FLASH_CONTROL_REG, 1 << 1) # FLASH_DISABLE_WP_STB + + def check_revision(self): + self.log.debug('Checking for compatible CPLD revision') + cpld_revision = self.regs.peek32(self.REVISION_REG) + if cpld_revision < self.cpld_min_revision: + self.log.error("Unexpected CPLD revision 0x{:X}".format(cpld_revision)) + return False + return True + + def get_start_addr(self): + return self.peek32(self.FLASH_CFM0_START_ADDR_REG) + + def get_end_addr(self): + return self.peek32(self.FLASH_CFM0_END_ADDR_REG) + + def is_memory_initialization_enabled(self): + return self.peek32(self.FLASH_STATUS_REG) & self.FLASH_MEM_INIT_ENABLED_MASK + + # expected value of 0x1110 indicates idle state of read, write, and erase + # routines (see function wait_for_idle for details), 0x0001 indicates the + # write protection is enabled + def check_reconfig_engine_status(self, expected_value=0x1111): + status = self.peek32(self.FLASH_STATUS_REG) + status = status & ~self.FLASH_MEM_INIT_ENABLED_MASK + if (status != expected_value): + self.log.error("Unexpected reconfig engine status 0x%08X" % status) + return False + return True + + def wait_for_idle(self, operation, timeout=350): + """ + Wait for the idle bit to assert for the given operation. + If the idle bit is not True before the timeout (given in ms), + return False. + """ + if operation == 'write': + status_bit = 1 << 12 # FLASH_WRITE_IDLE + elif operation == 'erase': + status_bit = 1 << 8 # FLASH_ERASE_IDLE + elif operation == 'read': + status_bit = 1 << 4 # FLASH_READ_IDLE + else: + self.log.error('Cannot wait for unknown operation {}'.format(operation)) + raise RuntimeError('Cannot wait for unknown operation {}'.format(operation)) + for _ in range(0, timeout): + status = self.peek32(self.FLASH_STATUS_REG) + if (status & status_bit): + return True + time.sleep(0.001) # 1 ms + return False + + def erase_flash_memory(self): + with self: + # check for sectors to be erased: + if self.is_memory_initialization_enabled(): + sectors = [2, 3, 4] + else: + sectors = [4] + # erase each sector individually + for sector in sectors: + # start erase + self.poke32(self.FLASH_CONTROL_REG, (1 << 4) | ((sector & 0x7) << 5)) + # wait for erase to finish + if not self.wait_for_idle('erase', timeout=350): + self.log.error('There was a timeout waiting for ' + 'Flash erase to complete!') + return False + return True + + def program_flash_memory(self, raw_data): + with self: + # write words one at a time + for i, data in enumerate(raw_data): + # status display + if (i%1000 == 0): + self.log.debug('%d%% written', i*4/self.file_size*100) + # write address and data + self.poke32(self.FLASH_ADDR_REG, self.cpld_start_address+i) + self.poke32(self.FLASH_WRITE_DATA_REG, data) + # start write operation + self.poke32(self.FLASH_CONTROL_REG, 1 << 3) + # wait for write to finish + if not self.wait_for_idle('write', timeout=2): + self.log.error('There was a timeout waiting for ' + 'Flash write to complete!') + return False + if not self.check_reconfig_engine_status(expected_value=0x1110): + return False + return True + + def verify_flash_memory(self, raw_data): + # read words one at a time + for i, data in enumerate(raw_data): + # status display + if (i%1000 == 0): + self.log.debug('%d%% done', i*4/self.file_size*100) + # write address + self.poke32(self.FLASH_ADDR_REG, self.cpld_start_address+i) + # start read operation + self.poke32(self.FLASH_CONTROL_REG, 1 << 2) + # wait for read to finish + if not self.wait_for_idle('read', timeout=1): + self.log.error('There was a timeout waiting for ' + 'Flash read to complete!') + return False + # read data from device + device_data = self.peek32(self.FLASH_READ_DATA_REG) + if (data != device_data): + self.log.error("Data mismatch! address %d, expected value 0x%08X," + " read value 0x%08X" % + (i+self.cpld_start_address, data, device_data)) + return False + return True + + def reverse_bits_in_byte(self, n): + result = 0 + for _ in range(8): + result <<= 1 + result |= n & 1 + n >>= 1 + return result + + def update(self, filename): + if not self.check_revision(): + return False + + self.log.debug('Checking CPLD image file') + self.file_size = os.path.getsize(filename) + self.cpld_start_address = self.get_start_addr() + cpld_end_address = self.get_end_addr() + expected_size = (cpld_end_address+1-self.cpld_start_address)*4 + if (self.file_size != expected_size): + self.log.error("Unexpected file size! Required size: %d bytes" % expected_size) + return False + + # Convert data from bytes to 32-bit words and reverse bit order + # to be compatible with Altera's on-chip flash IP + raw_data = [] + with open(filename, 'rb') as binary_file: + for _ in range(self.file_size//4): + number = 0 + for _ in range(4): + number <<= 8 + number |= self.reverse_bits_in_byte(int.from_bytes(binary_file.read(1), 'big')) + raw_data.append(number) + + if not self.check_reconfig_engine_status(): + return False + + self.log.debug('Erasing CPLD flash memory...') + if not (self.erase_flash_memory() + and self.check_reconfig_engine_status()): + self.log.error('There was an error while reprogramming the CPLD image. ' + 'Please program the CPLD again with a valid image before power ' + 'cycling the board to ensure it is in a valid state.') + return False + self.log.debug('CPLD flash memory erased.') + + self.log.debug('Programming flash memory...') + if not (self.program_flash_memory(raw_data) + and self.check_reconfig_engine_status()): + self.log.error('There was an error while reprogramming the CPLD image. ' + 'Please program the CPLD again with a valid image before power ' + 'cycling the board to ensure it is in a valid state.') + return False + self.log.debug('Flash memory programming complete.') + + self.log.debug('Verifying image in flash...') + if not (self.verify_flash_memory(raw_data) + and self.check_reconfig_engine_status()): + self.log.error('There was an error while reprogramming the CPLD image. ' + 'Please program the CPLD again with a valid image before power ' + 'cycling the board to ensure it is in a valid state.') + return False + self.log.debug('Flash image verification complete.') + + self.log.info('CPLD reprogrammed! Please power-cycle the device.') + + return True diff --git a/mpm/python/usrp_mpm/components.py b/mpm/python/usrp_mpm/components.py index 5d1a31325..a87dbdce7 100644 --- a/mpm/python/usrp_mpm/components.py +++ b/mpm/python/usrp_mpm/components.py @@ -7,6 +7,7 @@ MPM Component management """ import os +import re import shutil import subprocess from usrp_mpm.rpc_server import no_rpc @@ -30,6 +31,124 @@ class ZynqComponents(object): ########################################################################### # Component updating ########################################################################### + def _log_and_raise(self, logstr): + self.log.error(logstr) + raise RuntimeError(logstr) + + @classmethod + def _parse_dts_mpm_version_tag(cls, text): + """ parse a version line from the dts file. E.g. + "// mpm_version component1 3.4.5" will return + {"component1": (3, 4, 5)} """ + dts_version_re = re.compile(r'^// mpm_version\s+(?P<comp>\S+)\s+(?P<ver>\S+)$') + match = dts_version_re.match(text) + if match is None: + return (None, None) + + component = match[1] + version_list = [int(x, base=0) for x in match[2].split('.')] + return (component, tuple(version_list)) + + @classmethod + def _parse_dts_version_info_from_file(cls, filepath): + """ + parse all version informations from dts file and store in dict + a c-style comment in the dts file like this + // mpm_version component1 3.4.5 + // mpm_version other_component 3.5.0 + will return a dict: + {"component1": (3, 4, 5), "other_component": (3, 5, 0)"} + """ + suffix_current = "_current_version" + suffix_oldest = "_oldest_compatible_version" + + version_info = {} + with open(filepath) as f: + text = f.read() + for line in text.splitlines(): + component, version = cls._parse_dts_mpm_version_tag(line) + if not component: + continue + if component.endswith(suffix_oldest): + component = component[:-len(suffix_oldest)] + version_type = 'oldest' + elif component.endswith(suffix_current): + component = component[:-len(suffix_current)] + version_type = 'current' + else: + version_type = 'current' + if component not in version_info: + version_info[component] = {} + version_info[component][version_type] = version + return version_info + + def _verify_compatibility(self, filebasename, update_dict): + """ + check whether the given MPM compatibility matches the + version information stored in the FPGA DTS file + """ + def _get_version_string(versions_enum): + version_strings = [] + if 'current' in versions_enum: + version_strings.append("current: {}".format( + ".".join([str(x) for x in versions_enum['current']]))) + if 'oldest' in versions_enum: + version_strings.append("oldest compatible: {}".format( + ".".join([str(x) for x in versions_enum['oldest']]))) + return ', '.join(version_strings) + + + if update_dict.get('check_dts_for_compatibility'): + self.log.trace("Compatibility check MPM <-> FPGA via DTS enabled") + dtsfilepath = filebasename + '.dts' + if not os.path.exists(dtsfilepath): + self._log_and_raise("DTS file not found: {}".format(dtsfilepath)) + self.log.trace("Parse DTS file {} for version information"\ + .format(dtsfilepath)) + fpga_versions = self._parse_dts_version_info_from_file(dtsfilepath) + if not fpga_versions: + self._log_and_raise("no component version information in DTS file") + if 'compatibility' not in update_dict: + self._log_and_raise("MPM FPGA version infos not found") + mpm_versions = update_dict['compatibility'] + self.log.trace("DTS version infos: {}".format(fpga_versions)) + self.log.trace("MPM version infos: {}".format(mpm_versions)) + + try: + for component in mpm_versions.keys(): + # check for components that aren't available in the DTS file + if component in fpga_versions.keys(): + self.log.trace(f"check compatibility for: FPGA-{component}") + mpm_version = mpm_versions[component] + fpga_version = fpga_versions[component] + self.log.trace("mpm_version: current: {}, compatible: {}".format( + mpm_version['current'], mpm_version['oldest'])) + self.log.trace("fpga_version: current: {}, compatible: {}".format( + fpga_version['current'], fpga_version['oldest'])) + if mpm_version['oldest'][0] > fpga_version['current'][0]: + error = "Component {} is too old ({}, MPM version: {})".format( + component, + _get_version_string(fpga_version), + _get_version_string(mpm_version)) + self._log_and_raise(error) + elif mpm_version['current'][0] < fpga_version['oldest'][0]: + error = "Component {} is too new ({}, MPM version: {})".format( + component, + _get_version_string(fpga_version), + _get_version_string(mpm_version)) + self._log_and_raise(error) + self.log.trace(f"Component {component} is good!") + else: + self.log.warning(f"component {component} defined in "\ + f"MPM but not found in FPGA info, skipping.") + except RuntimeError as ex: + self._log_and_raise("MPM compatibility infos suggest that the "\ + "new bitfile is not compatible, skipping installation. {}"\ + .format(ex)) + else: + self.log.trace("Compatibility check MPM <-> FPGA is disabled") + return + @no_rpc def update_fpga(self, filepath, metadata): """ @@ -37,13 +156,19 @@ class ZynqComponents(object): :param filepath: path to new FPGA image :param metadata: Dictionary of strings containing metadata """ - self.log.trace("Updating FPGA with image at {} (metadata: `{}')" - .format(filepath, str(metadata))) - _, file_extension = os.path.splitext(filepath) + self.log.trace(f"Updating FPGA with image at {filepath}"\ + " (metadata: `{str(metadata)}')") + file_name, file_extension = os.path.splitext(filepath) + self.log.trace("file_name: {}".format(file_name)) # Cut off the period from the file extension file_extension = file_extension[1:].lower() - binfile_path = self.updateable_components['fpga']['path'].format( - self.device_info.get('product')) + if file_extension not in ['bit', 'bin']: + self._log_and_raise(f"Invalid FPGA bitfile: {filepath}") + binfile_path = self.updateable_components['fpga']['path']\ + .format(self.device_info.get('product')) + + self._verify_compatibility(file_name, self.updateable_components['fpga']) + if file_extension == "bit": self.log.trace("Converting bit to bin file and writing to {}" .format(binfile_path)) @@ -52,9 +177,7 @@ class ZynqComponents(object): elif file_extension == "bin": self.log.trace("Copying bin file to %s", binfile_path) shutil.copy(filepath, binfile_path) - else: - self.log.error("Invalid FPGA bitfile: %s", filepath) - raise RuntimeError("Invalid N3xx FPGA bitfile") + # RPC server will reload the periph manager after this. return True diff --git a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt index dfac467d0..b01d220df 100644 --- a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt +++ b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt @@ -28,9 +28,14 @@ set(USRP_MPM_DBMGR_FILES ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_update_cpld.py ${CMAKE_CURRENT_SOURCE_DIR}/mg_init.py ${CMAKE_CURRENT_SOURCE_DIR}/mg_periphs.py + ${CMAKE_CURRENT_SOURCE_DIR}/zbx.py ${CMAKE_CURRENT_SOURCE_DIR}/test.py ${CMAKE_CURRENT_SOURCE_DIR}/unknown.py ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_db_iface.py + ${CMAKE_CURRENT_SOURCE_DIR}/zbx_update_cpld.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_debug_db.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_if_test_cca.py ) list(APPEND USRP_MPM_FILES ${USRP_MPM_DBMGR_FILES}) set(USRP_MPM_FILES ${USRP_MPM_FILES} PARENT_SCOPE) diff --git a/mpm/python/usrp_mpm/dboard_manager/__init__.py b/mpm/python/usrp_mpm/dboard_manager/__init__.py index 58262025e..28a8ef80b 100644 --- a/mpm/python/usrp_mpm/dboard_manager/__init__.py +++ b/mpm/python/usrp_mpm/dboard_manager/__init__.py @@ -14,6 +14,11 @@ if not __simulated__: from .neon import Neon from .e31x_db import E31x_db from .eiscat import EISCAT + from .empty_slot import EmptySlot + from .zbx import ZBX from .test import test from .unknown import unknown from .dboard_iface import DboardIface + from .x4xx_db_iface import X4xxDboardIface + from .x4xx_debug_db import X4xxDebugDboard + from .x4xx_if_test_cca import X4xxIfTestCCA diff --git a/mpm/python/usrp_mpm/dboard_manager/base.py b/mpm/python/usrp_mpm/dboard_manager/base.py index be37a6264..978f1c5ae 100644 --- a/mpm/python/usrp_mpm/dboard_manager/base.py +++ b/mpm/python/usrp_mpm/dboard_manager/base.py @@ -24,6 +24,9 @@ class DboardManagerBase(object): # Very important: A list of PIDs that apply to the current device. Must be # list, even if there's only one entry. pids = [] + # tuple of id and name of the first revision, + # id and name of revisions are consecutive (2, B), (3, C), ... + first_revision = (1, 'A') # See PeriphManager.mboard_sensor_callback_map for a description. rx_sensor_callback_map = {} # See PeriphManager.mboard_sensor_callback_map for a description. @@ -91,6 +94,13 @@ class DboardManagerBase(object): """ self.log.debug("deinit() called, but not implemented.") + def tear_down(self): + """ + Tear down all members that need to be specially handled before + deconstruction. + """ + pass + def get_serial(self): """ Return this daughterboard's serial number as a string. Will return an @@ -98,6 +108,33 @@ class DboardManagerBase(object): """ return self.device_info.get("serial", "") + def get_revision(self): + """ + Return this daughterboard's revision number as integer. Will return + -1 if no revision can be found or revision is not an integer + """ + try: + return int(self.device_info.get('rev', '-1')) + except ValueError: + return -1 + + def get_revision_string(self): + """ + Converts revision number to string. + """ + return chr(ord(self.first_revision[1]) + + self.get_revision() + - self.first_revision[0]) + + ########################################################################## + # Clocking + ########################################################################## + def reset_clock(self, value): + """ + Called when the motherboard is reconfiguring its clocks. + """ + pass + def update_ref_clock_freq(self, freq, **kwargs): """ Call this function if the frequency of the reference clock changes. diff --git a/mpm/python/usrp_mpm/dboard_manager/dboard_iface.py b/mpm/python/usrp_mpm/dboard_manager/dboard_iface.py index 87bff846b..e100b02a2 100755..100644 --- a/mpm/python/usrp_mpm/dboard_manager/dboard_iface.py +++ b/mpm/python/usrp_mpm/dboard_manager/dboard_iface.py @@ -28,6 +28,32 @@ class DboardIface(object): if hasattr(self.mboard, 'log'): self.log = self.mboard.log.getChild("DboardIface") + def tear_down(self): + """ + Tear down all members that need to be specially handled before + deconstruction. + """ + # The mboard object is the periph_manager that has the dboard + # that in turn has this DboardIface. Breaking the reference + # cycle will make garbage collection easier. + self.mboard = None + + #################################################################### + # Power + # Enable and disable the DB's power rails + #################################################################### + def enable_daughterboard(self, enable = True): + """ + Enable or disable the daughterboard. + """ + raise NotImplementedError('DboardIface::enable_daughterboard() not supported!') + + def check_enable_daughterboard(self): + """ + Return the enable state of the daughterboard. + """ + raise NotImplementedError('DboardIface::check_enable_daughterboard() not supported!') + #################################################################### # CTRL SPI # CTRL SPI lines are connected to the CPLD of the DB if it exists @@ -42,18 +68,6 @@ class DboardIface(object): raise NotImplementedError('DboardIface::ctrl_spi_reset() not supported!') #################################################################### - # GPIO - # GPIO lines are used for high speed control of the DB - #################################################################### - def get_high_speed_gpio_ctrl_core(self): - """ - Return a GpioAtrCore4000 instance that controls the GPIO lines - interfacing the MB and DB - """ - raise NotImplementedError('DboardIface::get_high_speed_gpio_ctrl_core()' - ' not supported!') - - #################################################################### # Management Bus #################################################################### @@ -82,20 +96,28 @@ class DboardIface(object): """ raise NotImplementedError('DboardIface::set_if_freq() not supported!') + def get_if_freq(self, direction, channel): + """ + Gets the IF frequency of the ADC/DAC corresponding + to the specified channel of the DB. + """ + raise NotImplementedError('DboardIface::get_if_freq() not supported!') + + def enable_iq_swap(self, enable, direction, channel): + """ + Enable or disable swap of I and Q samples from the RFDCs. + """ + raise NotImplementedError('DboardIface::enable_iq_swap() not supported!') + + def get_sample_rate(self): + """ + Gets the sample rate of the RFDCs. + """ + raise NotImplementedError('DboardIface::get_sample_rate() not supported!') + def get_prc_rate(self): """ Returns the rate of the PLL Reference Clock (PRC) which is routed to the daughterboard. """ raise NotImplementedError('DboardIface::get_pll_ref_clock() not supported!') - - #################################################################### - # SPCC MPCC Control - #################################################################### - def get_protocol_cores(self): - """ - Returns all discovered protocols in SPCC and MPCC blocks on the - Daughterboard's CPLD in the form of SpiCore4000, I2cCore4000, - UartCore4000, and GpioAtrCore4000 - """ - raise NotImplementedError('DboardIface::get_protocol_cores() not supported!') diff --git a/mpm/python/usrp_mpm/dboard_manager/empty_slot.py b/mpm/python/usrp_mpm/dboard_manager/empty_slot.py new file mode 100644 index 000000000..997d3ac6a --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/empty_slot.py @@ -0,0 +1,37 @@ +# +# Copyright 2021 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Dummy daughterboard class for empty slots +""" +from usrp_mpm.dboard_manager import DboardManagerBase +from usrp_mpm.mpmlog import get_logger + +class EmptySlot(DboardManagerBase): + """ + DboardManager class for when a slot is empty + """ + ######################################################################### + # Overridables + # + # See DboardManagerBase for documentation on these fields + ######################################################################### + pids = [0x0] + ### End of overridables ################################################# + + def __init__(self, slot_idx, **kwargs): + DboardManagerBase.__init__(self, slot_idx, **kwargs) + self.log = get_logger("EmptyDB-{}".format(slot_idx)) + self.log.trace("Initializing Empty dboard, slot index %d", + self.slot_idx) + + def init(self, args): + """ + Execute necessary init dance to bring up dboard + """ + self.log.debug("init() called with args `{}'".format( + ",".join(['{}={}'.format(x, args[x]) for x in args]) + )) + return True diff --git a/mpm/python/usrp_mpm/dboard_manager/x4xx_db_iface.py b/mpm/python/usrp_mpm/dboard_manager/x4xx_db_iface.py new file mode 100644 index 000000000..d89236859 --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/x4xx_db_iface.py @@ -0,0 +1,144 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from usrp_mpm.sys_utils.db_flash import DBFlash +from usrp_mpm.sys_utils.gpio import Gpio +from usrp_mpm.dboard_manager import DboardIface +from usrp_mpm import lib # Pulls in everything from C++-land + +class X4xxDboardIface(DboardIface): + """ + X4xx DboardIface implementation + + slot_idx - The numerical ID of the daughterboard slot using this + interface (e.g. 0, 1) + motherboard - The instance of the motherboard class which implements + these controls + """ + # The device tree label for the bus to the DB's Management EEPROM + MGMT_EEPROM_DEVICE_LABEL = "e0004000.i2c" + + def __init__(self, slot_idx, motherboard): + super().__init__(slot_idx, motherboard) + self.db_cpld_iface = motherboard.ctrlport_regs.get_db_cpld_iface(self.slot_idx) + self._power_enable = Gpio('DB{}_PWR_EN'.format(slot_idx), Gpio.OUTPUT) + self._power_status = Gpio('DB{}_PWR_STATUS'.format(slot_idx), Gpio.INPUT) + + self.db_flash = DBFlash(slot_idx, log=self.log) + + def tear_down(self): + self.log.trace("Tearing down X4xx daughterboard...") + if self.db_flash: + self.db_flash.deinit() + super().tear_down() + + #################################################################### + # Power + # Enable and disable the DB's power rails + #################################################################### + def enable_daughterboard(self, enable=True): + """ + Enable or disable the daughterboard. + """ + if self.db_flash and not enable: + self.db_flash.deinit() + self._power_enable.set(enable) + self.mboard.cpld_control.enable_daughterboard(self.slot_idx, enable) + if self.db_flash and enable: + self.db_flash.init() + + def check_enable_daughterboard(self): + """ + Return the enable state of the daughterboard. + """ + return self._power_status.get() + + #################################################################### + # CTRL SPI + # CTRL SPI lines are connected to the CPLD of the DB if it exists + #################################################################### + def peek_db_cpld(self, addr): + return self.db_cpld_iface.peek32(addr) + + def poke_db_cpld(self, addr, val): + self.db_cpld_iface.poke32(addr, val) + + #################################################################### + # Management Bus + #################################################################### + + #################################################################### + # Calibration SPI + # The SPI/QSPI node used to interact with the DB + # Calibration EEPROM if it exists + #################################################################### + def get_cal_eeprom_spi_node(self, addr): + """ + Returns the QSPI node leading to the calibration EEPROM of the + given DB. + """ + chip_select = self.mboard.qspi_cs.get(self.db_name, None) + if chip_select is None: + raise RuntimeError('No QSPI chip select corresponds ' \ + 'with daughterboard {}'.format(self.db_name)) + return self.mboard.qspi_nodes[chip_select] + + #################################################################### + # MB Control + # Some of the MB settings may be controlled from the DB Driver + #################################################################### + def _find_converters(self, direction='both', channel='both'): + """ + Returns a list of (tile_id, block_id, is_dac) tuples describing + the data converters associated with a given channel and direction. + """ + return self.mboard.rfdc._find_converters(self.slot_idx, direction, channel) + + def set_if_freq(self, freq, direction='both', channel='both'): + """ + Use the rfdc_ctrl object to set the IF frequency of the ADCs and + DACs corresponding to the specified channels of the DB. + By default, all channels and directions will be set. + Returns True if the IF frequency was successfully set. + """ + for tile_id, block_id, is_dac in self._find_converters(direction, channel): + if not self.mboard.rfdc._rfdc_ctrl.set_if(tile_id, block_id, is_dac, freq): + return False + return True + + def get_if_freq(self, direction, channel): + """ + Gets the IF frequency of the ADC/DAC corresponding + to the specified channel of the DB. + """ + converters = self._find_converters(direction, channel) + assert len(converters) == 1, \ + 'Expected a single RFDC associated with {}{}. Instead found {}.' \ + .format(direction, channel, len(converters)) + (tile_id, block_id, is_dac) = converters[0] + return self.mboard.rfdc._rfdc_ctrl.get_nco_freq(tile_id, block_id, is_dac) + + def enable_iq_swap(self, enable, direction, channel): + """ + Enable or disable swap of I and Q samples from the RFDCs. + """ + for tile_id, block_id, is_dac in self._find_converters(direction, channel): + self.mboard.rfdc._rfdc_regs.enable_iq_swap(enable, self.slot_idx, block_id, is_dac) + + def get_sample_rate(self): + """ + Gets the sample rate of the RFDCs. + """ + return self.mboard.get_spll_freq() + + def get_prc_rate(self): + """ + Returns the rate of the PLL Reference Clock (PRC) which is + routed to the daughterboard. + Note: The ref clock will change if the sample clock frequency + is modified. + """ + return self.mboard.get_prc_rate() diff --git a/mpm/python/usrp_mpm/dboard_manager/x4xx_debug_db.py b/mpm/python/usrp_mpm/dboard_manager/x4xx_debug_db.py new file mode 100644 index 000000000..f5e023229 --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/x4xx_debug_db.py @@ -0,0 +1,152 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Debug dboard implementation module +""" + +from usrp_mpm.dboard_manager import DboardManagerBase +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.sys_utils.gpio import Gpio + +class DebugDboardSignalPath: + def __init__(self, slot_idx, path, adc_indexes, dac_indexes, loopback): + self.log = get_logger("X4xxDebugDboard-{}-path-{}".format(slot_idx, path)) + + self.rxa2_led = Gpio("DB{}_RX{}2_LED".format(slot_idx, path), Gpio.OUTPUT, 0) + self.rxa_led = Gpio("DB{}_RX{}_LED".format(slot_idx, path), Gpio.OUTPUT, 0) + self.txa_led = Gpio("DB{}_TX{}_LED".format(slot_idx, path), Gpio.OUTPUT, 0) + + self.trx_ctrl = Gpio("DB{}_TRX{}_CTRL".format(slot_idx, path), Gpio.OUTPUT, 0) + self.rx_mux_ctrl = Gpio("DB{}_RX{}_MUX_CTRL".format(slot_idx, path), Gpio.OUTPUT, 0) + self.tx_mux_ctrl = Gpio("DB{}_TX{}_MUX_CTRL".format(slot_idx, path), Gpio.OUTPUT, 0) + + self._adc_indices = adc_indexes + self._dac_indices = dac_indexes + self._loopback = loopback + self._path = path + + def configure(self, adc, dac, loopback): + """ + Configure this path with the appropriate settings + """ + if adc.lower() not in self._adc_indices: + error_msg = "Could not find ADC {} on path {}. Possible ADCs: {}".format( + adc, self._path, ", ".join(self._adc_indices.keys()) + ) + self.log.error(error_msg) + raise RuntimeError(error_msg) + + if dac.lower() not in self._dac_indices: + error_msg = "Could not find DAC {} on path {}. Possible DACs: {}".format( + dac, self._path, ", ".join(self._dac_indices.keys()) + ) + self.log.error(error_msg) + raise RuntimeError(error_msg) + + self.rx_mux_ctrl.set(self._adc_indices[adc.lower()]) + self.tx_mux_ctrl.set(self._dac_indices[dac.lower()]) + self.trx_ctrl.set(self._loopback if loopback else not self._loopback) + + +class X4xxDebugDboard(DboardManagerBase): + """ + Holds all dboard specific information and methods of the X4xx debug dboard + """ + ######################################################################### + # Overridables + # + # See DboardManagerBase for documentation on these fields + ######################################################################### + pids = [0x4001] + ### End of overridables ################################################# + + def __init__(self, slot_idx, **kwargs): + DboardManagerBase.__init__(self, slot_idx, **kwargs) + self.log = get_logger("X4xxDebugDboard-{}".format(slot_idx)) + self.log.trace("Initializing X4xxDebug daughterboard, slot index %d", + self.slot_idx) + + # Interface with MB HW + if 'db_iface' not in kwargs: + self.log.error("Required DB Iface was not provided!") + raise RuntimeError("Required DB Iface was not provided!") + self.db_iface = kwargs['db_iface'] + + # Power on the card + self.db_iface.enable_daughterboard(enable=True) + if not self.db_iface.check_enable_daughterboard(): + self.db_iface.enable_daughterboard(enable=False) + self.log.error('Debug dboard {} power up failed'.format(self.slot_idx)) + raise RuntimeError('Debug dboard {} power up failed'.format(self.slot_idx)) + + self._paths = { + "a": DebugDboardSignalPath( + slot_idx, + "A", + { + "adc0": 1, + "adc2": 0, + }, + { + "dac0": 1, + "dac2": 0, + }, + 1 # TRXA_CTRL=1 enables loopback + ), + "b": DebugDboardSignalPath( + slot_idx, + "B", + { + "adc3": 1, + "adc1": 0, + }, + { + "dac3": 1, + "dac1": 0, + }, + 0 # TRXB_CTRL=0 enables loopback + ), + } + + + # TODO: Configure the correct RFDC settings for this board + #if not self.db_iface.disable_mixer(): + # raise RuntimeError("Received an error disabling the mixer for slot_idx={}".format(slot_idx)) + + def init(self, args): + """ + Execute necessary init dance to bring up dboard + """ + self.log.debug("init() called with args `{}'".format( + ",".join(['{}={}'.format(x, args[x]) for x in args]) + )) + self.config_path("a", "adc0", "dac0", 0) + self.config_path("b", "adc1", "dac1", 0) + return True + + def deinit(self): + pass + + def tear_down(self): + self.db_iface.tear_down() + + def config_path(self, path, adc, dac, loopback): + """ + Configure the signal paths on the daughterboard. + path - Select between front panel connectors A or B. The two paths are unconnected. + adc - Select which ADC to connect to the path (adc0 or adc2 on A, adc1 or adc3 on B) + dac - Select which DAC to connect to the path (dac0 or dac2 on A, dac1 or dac3 on B) + loopback - Whether to enable loopback (1) or route the ADC/DACs to the front panel (0) + + Example MPM shell usage: + > db_0_config_path a adc0 dac2 1 + """ + if path.lower() not in self._paths: + self.log.error("Tried to configure path {} which does not exist!".format(path)) + raise RuntimeError("Tried to configure path {} which does not exist!".format(path)) + + path = self._paths[path.lower()] + path.configure(adc, dac, int(loopback)) diff --git a/mpm/python/usrp_mpm/dboard_manager/x4xx_if_test_cca.py b/mpm/python/usrp_mpm/dboard_manager/x4xx_if_test_cca.py new file mode 100644 index 000000000..c435ddb1c --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/x4xx_if_test_cca.py @@ -0,0 +1,167 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +IF Test CCA implementation module +""" +from usrp_mpm.dboard_manager import DboardManagerBase +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.sys_utils.gpio import Gpio + +class X4xxIfTestCCA(DboardManagerBase): + """ + Holds all dboard specific information and methods of the X4xx IF Test CCA + """ + ######################################################################### + # Overridables + # + # See DboardManagerBase for documentation on these fields + ######################################################################### + pids = [0x4006] + ### End of overridables ################################################# + + def __init__(self, slot_idx, **kwargs): + DboardManagerBase.__init__(self, slot_idx, **kwargs) + self.log = get_logger("X4xxIfTestCCA-{}".format(slot_idx)) + self.log.trace("Initializing X4xxIfTestCCA, slot index %d", + self.slot_idx) + + # Interface with MB HW + if 'db_iface' not in kwargs: + self.log.error("Required DB Iface was not provided!") + raise RuntimeError("Required DB Iface was not provided!") + self.db_iface = kwargs['db_iface'] + + # Power on the card + self.db_iface.enable_daughterboard(enable=True) + if not self.db_iface.check_enable_daughterboard(): + self.db_iface.enable_daughterboard(enable=False) + self.log.error('IF Test CCA {} power up failed'.format(self.slot_idx)) + raise RuntimeError('IF Test CCA {} power up failed'.format(self.slot_idx)) + + # [boolean for stage 1 mux , boolean for stage 2 mux] + self._adc_mux_settings = { + "adc0" : [0, 0], + "adc1" : [1, 1], + "adc2" : [1, 0], + "adc3" : [0, 1], + } + + self._dac_mux_settings = { + "dac0" : [1, 0], + "dac1" : [1, 1], + "dac2" : [0, 0], + "dac3" : [0, 1], + } + + # There are 4 possible Tx (DAC) streams that are available to choose + # to export to the SMA TX port using a 2-stage hardware mux. + + # Choose between 0 and 2 OR 1 and 3 + self.tx_0_2_1_3_mux_ctrl = Gpio("DB{}_TX0_2p_1_3n".format(slot_idx), Gpio.OUTPUT, 0) + # Choose between 0 OR 2 + self.tx_0_2_mux_ctrl = Gpio("DB{}_TX_MUX_0p_2n".format(slot_idx), Gpio.OUTPUT, 0) + # Choose between 1 OR 3 + self.tx_1_3_mux_ctrl = Gpio("DB{}_TX_MUX_1p_3n".format(slot_idx), Gpio.OUTPUT, 0) + + # The signal from the SMA RX port can be directed to one of the 4 + # available Rx (ADC) streams using a 2-stage hardware mux. + + # Choose between 0 and 2 OR 1 and 3 + self.rx_0_2_1_3_mux_ctrl = Gpio("DB{}_RX0_2p_1_3n".format(slot_idx), Gpio.OUTPUT, 0) + # Choose between 0 OR 2 + self.rx_0_2_mux_ctrl = Gpio("DB{}_RX_MUX_0p_2n".format(slot_idx), Gpio.OUTPUT, 0) + # Choose between 1 OR 3 + self.rx_1_3_mux_ctrl = Gpio("DB{}_RX_MUX_1p_3n".format(slot_idx), Gpio.OUTPUT, 0) + + self._tx_path = "" + self._rx_path = "" + + # Controls to load the power supplies on the daughterboard. Enabling + # these will increase the power draw of the daughterboard. + self.enable_1v8_load = Gpio("DB{}_1V8_LOAD".format(slot_idx), Gpio.OUTPUT, 0) + self.enable_2v5_load = Gpio("DB{}_2V5_LOAD".format(slot_idx), Gpio.OUTPUT, 0) + self.enable_3v3_load = Gpio("DB{}_3V3_LOAD".format(slot_idx), Gpio.OUTPUT, 0) + self.enable_3v3_mcu_load = Gpio("DB{}_3V3_MCU_LOAD".format(slot_idx), Gpio.OUTPUT, 0) + self.enable_3v7_load = Gpio("DB{}_3V7_LOAD".format(slot_idx), Gpio.OUTPUT, 0) + self.enable_12v_load = Gpio("DB{}_12V_LOAD".format(slot_idx), Gpio.OUTPUT, 0) + + # Control to choose between DAC output or MB VCM signals as the VCM + # signal to use on board. + self.disable_vcm_dac = Gpio("DB{}_VCM_MB_nDAC".format(slot_idx), Gpio.OUTPUT, 0) + + # Control to choose which MB clock to output to the SMA Clock port. + # Choices are BaseRefClk and PllRefClk + self.disable_vcm_dac = Gpio("DB{}_REF_CLK_SEL_USR".format(slot_idx), Gpio.OUTPUT, 0) + + + def init(self, args): + """ + Execute necessary init dance to bring up dboard + """ + self.log.debug("init() called with args `{}'".format( + ",".join(['{}={}'.format(x, args[x]) for x in args]) + )) + self.config_tx_path("dac0") + self.config_rx_path("adc0") + return True + + def deinit(self): + pass + + def tear_down(self): + self.db_iface.tear_down() + + def config_tx_path(self, dac): + """ + Configure the tx signal path on the daughterboard. + dac - Select which DAC to connect to the Tx path (dac0 through dac3) + + Example MPM shell usage: + > db_0_config_tx_path dac2 + """ + + if dac.lower() not in self._dac_mux_settings: + error_msg = "Could not find DAC {}. Possible DACs: {}".format( + dac, ", ".join(self._dac_mux_settings.keys()) + ) + self.log.error(error_msg) + raise RuntimeError(error_msg) + + # Only one of the following setting really matters; simplify logic + # by toggling both since the stage 2 decides what gets connected. + self.tx_0_2_mux_ctrl.set(self._dac_mux_settings[dac.lower()][0]) + self.tx_1_3_mux_ctrl.set(self._dac_mux_settings[dac.lower()][0]) + self.tx_0_2_1_3_mux_ctrl.set(self._dac_mux_settings[dac.lower()][1]) + self._tx_path = dac.upper() + + def get_tx_path(self): + return self._tx_path + + def config_rx_path(self, adc): + """ + Configure the rx signal path on the daughterboard. + adc - Select which ADC to connect to the Rx path (adc0 through adc3) + + Example MPM shell usage: + > db_0_config_rx_path adc0 + """ + + if adc.lower() not in self._adc_mux_settings: + error_msg = "Could not find ADC {}. Possible ADCs: {}".format( + adc, ", ".join(self._adc_mux_settings.keys()) + ) + self.log.error(error_msg) + raise RuntimeError(error_msg) + + self.rx_0_2_1_3_mux_ctrl.set(self._adc_mux_settings[adc.lower()][1]) + # Only one of the following setting really matters; simplify logic + # by toggling both + self.rx_0_2_mux_ctrl.set(self._adc_mux_settings[adc.lower()][0]) + self.rx_1_3_mux_ctrl.set(self._adc_mux_settings[adc.lower()][0]) + self._rx_path = adc.upper() + + def get_rx_path(self): + return self._rx_path diff --git a/mpm/python/usrp_mpm/dboard_manager/zbx.py b/mpm/python/usrp_mpm/dboard_manager/zbx.py new file mode 100644 index 000000000..8343119c8 --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/zbx.py @@ -0,0 +1,461 @@ +# +# Copyright 2019-2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +ZBX dboard implementation module +""" + +import time +from usrp_mpm import tlv_eeprom +from usrp_mpm.dboard_manager import DboardManagerBase +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.chips.ic_reg_maps import zbx_cpld_regs_t +from usrp_mpm.periph_manager.x4xx_periphs import get_temp_sensor +from usrp_mpm.sys_utils.udev import get_eeprom_paths_by_symbol + +############################################################################### +# Helpers +############################################################################### +def parse_encoded_git_hash(encoded): + """ + Helper function: Unpacks the git hash encoded in the ZBX CPLD image into + the git hash and a dirty flag. + """ + git_hash = encoded & 0x0FFFFFFF + tree_dirty = ((encoded & 0xF0000000) > 0) + dirtiness_qualifier = 'dirty' if tree_dirty else 'clean' + return (git_hash, dirtiness_qualifier) + + +# pylint: disable=too-few-public-methods +class EepromTagMap: + """ + Defines the tagmap for EEPROMs matching this magic. + The tagmap is a dictionary mapping an 8-bit tag to a NamedStruct instance. + The canonical list of tags and the binary layout of the associated structs + is defined in mpm/tools/tlv_eeprom/usrp_eeprom.h. Only the subset relevant + to MPM are included below. + """ + magic = 0x55535250 + tagmap = { + # 0x10: usrp_eeprom_board_info + 0x10: tlv_eeprom.NamedStruct('< H H H 7s 1x', + ['pid', 'rev', 'rev_compat', 'serial']), + } + + +############################################################################### +# Main dboard control class +############################################################################### +class ZBX(DboardManagerBase): + """ + Holds all dboard specific information and methods of the ZBX dboard + """ + ######################################################################### + # Overridables + # + # See DboardManagerBase for documentation on these fields + ######################################################################### + pids = [0x4002] + rx_sensor_callback_map = { + 'temperature': 'get_rf_temp_sensor', + } + tx_sensor_callback_map = { + 'temperature': 'get_rf_temp_sensor', + } + ### End of overridables ################################################# + + # Daughterboard required rev_compat value, this is compared against + # rev_compat in the eeprom + # Change only on breaking changes + DBOARD_REQUIRED_COMPAT_REV = 0x1 + + # CPLD compatibility revision + # Change this revision only on breaking changes. + REQ_OLDEST_COMPAT_REV = 0x20110611 + REQ_COMPAT_REV = 0x20110611 + + ######################################################################### + # MPM Initialization + ######################################################################### + def __init__(self, slot_idx, **kwargs): + DboardManagerBase.__init__(self, slot_idx, **kwargs) + self.log = get_logger("ZBX-{}".format(slot_idx)) + self.log.trace("Initializing ZBX daughterboard, slot index %d", + self.slot_idx) + + # local variable to track if PLL ref clock is enabled for the CPLD logic + self._clock_enabled = False + + # Interface with MB HW + if 'db_iface' not in kwargs: + self.log.error("Required DB Iface was not provided!") + raise RuntimeError("Required DB Iface was not provided!") + self.db_iface = kwargs['db_iface'] + + self.eeprom_symbol = f"db{slot_idx}_eeprom" + eeprom = self._get_eeprom() + if eeprom["rev_compat"] != self.DBOARD_REQUIRED_COMPAT_REV: + err = f"Found ZBX rev_compat 0x{eeprom['rev_compat']:02x}," \ + f" required is 0x{self.DBOARD_REQUIRED_COMPAT_REV:02x}" + self.log.error(err) + raise RuntimeError(err) + + # Initialize daughterboard CPLD control + self.poke_cpld = self.db_iface.poke_db_cpld + self.peek_cpld = self.db_iface.peek_db_cpld + self.regs = zbx_cpld_regs_t() + self._spi_addr = self.regs.SPI_READY_addr + self._enable_base_power() + # Check register map compatibility + self._check_compat_version() + self.log.debug("ZBX CPLD build git hash: %s", self._get_cpld_git_hash()) + # Power up the DB + self._enable_power() + # enable PLL reference clock + self.reset_clock(False) + self._cpld_set_safe_defaults() + + def _get_eeprom(self): + """ + Return the eeprom data. + """ + path = get_eeprom_paths_by_symbol(self.eeprom_symbol)[self.eeprom_symbol] + eeprom, _ = tlv_eeprom.read_eeprom(path, EepromTagMap.tagmap, EepromTagMap.magic, None) + return eeprom + + def _enable_base_power(self, enable=True): + """ + Enables or disables power to the DB which enables communication to DB CPLD + """ + if enable: + self.db_iface.enable_daughterboard(enable=True) + if not self.db_iface.check_enable_daughterboard(): + self.db_iface.enable_daughterboard(enable=False) + self.log.error('ZBX {} power up failed'.format(self.slot_idx)) + raise RuntimeError('ZBX {} power up failed'.format(self.slot_idx)) + else: # disable + # Removing power from the CPLD will set all the the output pins to open and the + # supplies default to disabled on power up. + self.db_iface.enable_daughterboard(enable=False) + if self.db_iface.check_enable_daughterboard(): + self.log.error('ZBX {} power down failed'.format(self.slot_idx)) + + def _enable_power(self, enable=True): + """ Enables or disables power switches internal to the DB CPLD """ + self.regs.ENABLE_TX_POS_7V0 = self.regs.ENABLE_TX_POS_7V0_t(int(enable)) + self.regs.ENABLE_RX_POS_7V0 = self.regs.ENABLE_RX_POS_7V0_t(int(enable)) + self.regs.ENABLE_POS_3V3 = self.regs.ENABLE_POS_3V3_t(int(enable)) + self.poke_cpld( + self.regs.ENABLE_POS_3V3_addr, + self.regs.get_reg(self.regs.ENABLE_POS_3V3_addr)) + + def _check_compat_version(self): + """ Check compatibility of DB CPLD image and SW regmap """ + compat_revision_addr = self.regs.OLDEST_COMPAT_REVISION_addr + cpld_oldest_compat_revision = self.peek_cpld(compat_revision_addr) + if cpld_oldest_compat_revision < self.REQ_OLDEST_COMPAT_REV: + err_msg = ( + f'DB CPLD oldest compatible revision 0x{cpld_oldest_compat_revision:x}' + f' is out of date, the required revision is 0x{self.REQ_OLDEST_COMPAT_REV:x}. ' + f'Update your CPLD image.') + self.log.error(err_msg) + raise RuntimeError(err_msg) + if cpld_oldest_compat_revision > self.REQ_OLDEST_COMPAT_REV: + err_msg = ( + f'DB CPLD oldest compatible revision 0x{cpld_oldest_compat_revision:x}' + f' is newer than the expected revision 0x{self.REQ_OLDEST_COMPAT_REV:x}.' + ' Downgrade your CPLD image or update MPM.') + self.log.error(err_msg) + raise RuntimeError(err_msg) + + if not self.has_compat_version(self.REQ_COMPAT_REV): + err_msg = ( + "ZBX DB CPLD revision is too old. Update your" + f" CPLD image to at least 0x{self.REQ_COMPAT_REV:08x}.") + self.log.error(err_msg) + raise RuntimeError(err_msg) + + def has_compat_version(self, min_required_version): + """ + Check for a minimum required version. + """ + cpld_image_compat_revision = self.peek_cpld(self.regs.REVISION_addr) + return cpld_image_compat_revision >= min_required_version + + # pylint: disable=too-many-statements + def _cpld_set_safe_defaults(self): + """ + Set the CPLD into a safe state. + """ + cpld_regs = zbx_cpld_regs_t() + # We un-configure some registers to force a change later. None of these + # values get written to the CPLD! + cpld_regs.RF0_OPTION = cpld_regs.RF0_OPTION.RF0_OPTION_FPGA_STATE + cpld_regs.RF1_OPTION = cpld_regs.RF1_OPTION.RF1_OPTION_FPGA_STATE + cpld_regs.SW_RF0_CONFIG = 255 + cpld_regs.SW_RF1_CONFIG = 255 + cpld_regs.TX0_DSA1[0] = 0 + cpld_regs.TX0_DSA2[0] = 0 + cpld_regs.RX0_DSA1[0] = 0 + cpld_regs.RX0_DSA2[0] = 0 + cpld_regs.RX0_DSA3_A[0] = 0 + cpld_regs.RX0_DSA3_B[0] = 0 + cpld_regs.save_state() + # Now all the registers we touch will be enumerated by get_changed_addrs() + # Everything below *will* get written to the CPLD: + # ATR control + cpld_regs.RF0_OPTION = cpld_regs.RF0_OPTION.RF0_OPTION_SW_DEFINED + cpld_regs.RF1_OPTION = cpld_regs.RF1_OPTION.RF1_OPTION_SW_DEFINED + # Back to state 0 and sw-defined. That means nothing will get configured + # until UHD boots again. + cpld_regs.SW_RF0_CONFIG = 0 + cpld_regs.SW_RF1_CONFIG = 0 + # TX0 path control + cpld_regs.TX0_IF2_1_2[0] = cpld_regs.TX0_IF2_1_2[0].TX0_IF2_1_2_FILTER_2 + cpld_regs.TX0_IF1_3[0] = cpld_regs.TX0_IF1_3[0].TX0_IF1_3_FILTER_0_3 + cpld_regs.TX0_IF1_4[0] = cpld_regs.TX0_IF1_4[0].TX0_IF1_4_TERMINATION + cpld_regs.TX0_IF1_5[0] = cpld_regs.TX0_IF1_5[0].TX0_IF1_5_TERMINATION + cpld_regs.TX0_IF1_6[0] = cpld_regs.TX0_IF1_6[0].TX0_IF1_6_FILTER_0_3 + cpld_regs.TX0_7[0] = cpld_regs.TX0_7[0].TX0_7_TERMINATION + cpld_regs.TX0_RF_8[0] = cpld_regs.TX0_RF_8[0].TX0_RF_8_RF_1 + cpld_regs.TX0_RF_9[0] = cpld_regs.TX0_RF_9[0].TX0_RF_9_RF_1 + cpld_regs.TX0_ANT_10[0] = cpld_regs.TX0_ANT_10[0].TX0_ANT_10_BYPASS_AMP + cpld_regs.TX0_ANT_11[0] = cpld_regs.TX0_ANT_11[0].TX0_ANT_11_BYPASS_AMP + cpld_regs.TX0_LO_13[0] = cpld_regs.TX0_LO_13[0].TX0_LO_13_INTERNAL + cpld_regs.TX0_LO_14[0] = cpld_regs.TX0_LO_14[0].TX0_LO_14_INTERNAL + # TX1 path control + cpld_regs.TX1_IF2_1_2[0] = cpld_regs.TX1_IF2_1_2[0].TX1_IF2_1_2_FILTER_2 + cpld_regs.TX1_IF1_3[0] = cpld_regs.TX1_IF1_3[0].TX1_IF1_3_FILTER_0_3 + cpld_regs.TX1_IF1_4[0] = cpld_regs.TX1_IF1_4[0].TX1_IF1_4_TERMINATION + cpld_regs.TX1_IF1_5[0] = cpld_regs.TX1_IF1_5[0].TX1_IF1_5_TERMINATION + cpld_regs.TX1_IF1_6[0] = cpld_regs.TX1_IF1_6[0].TX1_IF1_6_FILTER_0_3 + cpld_regs.TX1_7[0] = cpld_regs.TX1_7[0].TX1_7_TERMINATION + cpld_regs.TX1_RF_8[0] = cpld_regs.TX1_RF_8[0].TX1_RF_8_RF_1 + cpld_regs.TX1_RF_9[0] = cpld_regs.TX1_RF_9[0].TX1_RF_9_RF_1 + cpld_regs.TX1_ANT_10[0] = cpld_regs.TX1_ANT_10[0].TX1_ANT_10_BYPASS_AMP + cpld_regs.TX1_ANT_11[0] = cpld_regs.TX1_ANT_11[0].TX1_ANT_11_BYPASS_AMP + cpld_regs.TX1_LO_13[0] = cpld_regs.TX1_LO_13[0].TX1_LO_13_INTERNAL + cpld_regs.TX1_LO_14[0] = cpld_regs.TX1_LO_14[0].TX1_LO_14_INTERNAL + # RX0 path control + cpld_regs.RX0_ANT_1[0] = cpld_regs.RX0_ANT_1[0].RX0_ANT_1_TERMINATION + cpld_regs.RX0_2[0] = cpld_regs.RX0_2[0].RX0_2_LOWBAND + cpld_regs.RX0_RF_3[0] = cpld_regs.RX0_RF_3[0].RX0_RF_3_RF_1 + cpld_regs.RX0_4[0] = cpld_regs.RX0_4[0].RX0_4_LOWBAND + cpld_regs.RX0_IF1_5[0] = cpld_regs.RX0_IF1_5[0].RX0_IF1_5_FILTER_1 + cpld_regs.RX0_IF1_6[0] = cpld_regs.RX0_IF1_6[0].RX0_IF1_6_FILTER_1 + cpld_regs.RX0_LO_9[0] = cpld_regs.RX0_LO_9[0].RX0_LO_9_INTERNAL + cpld_regs.RX0_LO_10[0] = cpld_regs.RX0_LO_10[0].RX0_LO_10_INTERNAL + cpld_regs.RX0_RF_11[0] = cpld_regs.RX0_RF_11[0].RX0_RF_11_RF_3 + # RX1 path control + cpld_regs.RX1_ANT_1[0] = cpld_regs.RX1_ANT_1[0].RX1_ANT_1_TERMINATION + cpld_regs.RX1_2[0] = cpld_regs.RX1_2[0].RX1_2_LOWBAND + cpld_regs.RX1_RF_3[0] = cpld_regs.RX1_RF_3[0].RX1_RF_3_RF_1 + cpld_regs.RX1_4[0] = cpld_regs.RX1_4[0].RX1_4_LOWBAND + cpld_regs.RX1_IF1_5[0] = cpld_regs.RX1_IF1_5[0].RX1_IF1_5_FILTER_1 + cpld_regs.RX1_IF1_6[0] = cpld_regs.RX1_IF1_6[0].RX1_IF1_6_FILTER_1 + cpld_regs.RX1_LO_9[0] = cpld_regs.RX1_LO_9[0].RX1_LO_9_INTERNAL + cpld_regs.RX1_LO_10[0] = cpld_regs.RX1_LO_10[0].RX1_LO_10_INTERNAL + cpld_regs.RX1_RF_11[0] = cpld_regs.RX1_RF_11[0].RX1_RF_11_RF_3 + # TX DSA + cpld_regs.TX0_DSA1[0] = 31 + cpld_regs.TX0_DSA2[0] = 31 + # RX DSA + cpld_regs.RX0_DSA1[0] = 15 + cpld_regs.RX0_DSA2[0] = 15 + cpld_regs.RX0_DSA3_A[0] = 15 + cpld_regs.RX0_DSA3_B[0] = 15 + for addr in cpld_regs.get_changed_addrs(): + self.poke_cpld(addr, cpld_regs.get_reg(addr)) + # pylint: enable=too-many-statements + + ######################################################################### + # UHD (De-)Initialization + ######################################################################### + def init(self, args): + """ + Execute necessary init dance to bring up dboard. This happens when a UHD + session starts. + """ + self.log.debug("init() called with args `{}'".format( + ",".join(['{}={}'.format(x, args[x]) for x in args]) + )) + return True + + def deinit(self): + """ + De-initialize after UHD session completes + """ + self.log.debug("Setting CPLD back to safe defaults after UHD session.") + self._cpld_set_safe_defaults() + + def tear_down(self): + self.db_iface.tear_down() + + ######################################################################### + # API calls needed by the zbx_dboard driver + ######################################################################### + def enable_iq_swap(self, enable, trx, channel): + """ + Turn on IQ swapping in the RFDC + """ + self.db_iface.enable_iq_swap(enable, trx, channel) + + def get_dboard_sample_rate(self): + """ + Return the RFDC rate. This is usually a big number in the 3 GHz range. + """ + return self.db_iface.get_sample_rate() + + def get_dboard_prc_rate(self): + """ + Return the PRC rate. The CPLD and LOs are clocked with this. + """ + return self.db_iface.get_prc_rate() + + def _has_compat_version(self, min_required_version): + """ + Check for a minimum required version. + """ + cpld_image_compat_revision = self.peek_cpld(self.regs.REVISION_addr) + return cpld_image_compat_revision >= min_required_version + + def _get_cpld_git_hash(self): + """ + Trace build of MB CPLD + """ + git_hash_rb = self.peek_cpld(self.regs.GIT_HASH_addr) + (git_hash, dirtiness_qualifier) = parse_encoded_git_hash(git_hash_rb) + return "{:07x} ({})".format(git_hash, dirtiness_qualifier) + + def reset_clock(self, value): + """ + Disable PLL reference clock to enable SPLL reconfiguration + + Puts the clock into reset if value is True, takes it out of reset + otherwise. + """ + if self._clock_enabled != bool(value): + return + addr = self.regs.get_addr("PLL_REF_CLOCK_ENABLE") + enum = self.regs.PLL_REF_CLOCK_ENABLE_t + if value: + reg_value = enum.PLL_REF_CLOCK_ENABLE_DISABLE.value + else: + reg_value = enum.PLL_REF_CLOCK_ENABLE_ENABLE.value + self.poke_cpld(addr, reg_value) + self._clock_enabled = not bool(value) + + ######################################################################### + # LO SPI API + # + # We keep a LO peek/poke interface for debugging purposes. + ######################################################################### + def _wait_for_spi_ready(self, timeout): + """ Returns False if a timeout occurred. timeout is in ms """ + for _ in range(timeout): + if (self.peek_cpld(self._spi_addr) >> self.regs.SPI_READY_shift) \ + & self.regs.SPI_READY_mask: + return True + time.sleep(0.001) + return False + + def _lo_spi_send_tx(self, lo_name, write, addr, data=None): + """ Wait for SPI Ready and setup the TX data for a LO SPI transaction """ + if not self._wait_for_spi_ready(timeout=100): + self.log.error('Timeout before LO SPI transaction waiting for SPI Ready') + raise RuntimeError('Timeout before LO SPI transaction waiting for SPI Ready') + lo_enum_name = 'LO_SELECT_' + lo_name.upper() + assert hasattr(self.regs.LO_SELECT_t, lo_enum_name), \ + "Invalid LO name: {}".format(lo_name) + self.regs.LO_SELECT = getattr(self.regs.LO_SELECT_t, lo_enum_name) + if write: + self.regs.READ_FLAG = self.regs.READ_FLAG_t.READ_FLAG_WRITE + else: + self.regs.READ_FLAG = self.regs.READ_FLAG_t.READ_FLAG_READ + if data is not None: + self.regs.DATA = data + else: + self.regs.DATA = 0 + self.regs.ADDRESS = addr + self.regs.START_TRANSACTION = \ + self.regs.START_TRANSACTION_t.START_TRANSACTION_ENABLE + self.poke_cpld(self._spi_addr, self.regs.get_reg(self._spi_addr)) + + def _lo_spi_check_status(self, lo_name, addr, write=False): + """ Wait for SPI Ready and check the success of the LO SPI transaction """ + # SPI Ready indicates that the previous transaction has completed + # and the RX data is ready to be consumed + if not write and not self._wait_for_spi_ready(timeout=100): + self.log.error('Timeout after LO SPI transaction waiting for SPI Ready') + raise RuntimeError('Timeout after LO SPI transaction waiting for SPI Ready') + # If the address or CS are not the same as what we set, there + # was interference during the SPI transaction + lo_select = self.regs.LO_SELECT.name[len('LO_SELECT_'):] + if self.regs.ADDRESS != addr or lo_select != lo_name.upper(): + self.log.error('SPI transaction to LO failed!') + raise RuntimeError('SPI transaction to LO failed!') + + def _lo_spi_get_rx(self): + """ Return RX data read from the LO SPI transaction """ + spi_reg = self.peek_cpld(self._spi_addr) + return (spi_reg >> self.regs.DATA_shift) & self.regs.DATA_mask + + def peek_lo_spi(self, lo_name, addr): + """ Perform a register read access to an LO via SPI """ + self._lo_spi_send_tx(lo_name=lo_name, write=False, addr=addr) + self._lo_spi_check_status(lo_name, addr) + return self._lo_spi_get_rx() + + def poke_lo_spi(self, lo_name, addr, val): + """ Perform a register write access to an LO via SPI """ + self._lo_spi_send_tx(lo_name=lo_name, write=True, addr=addr, data=val) + self._lo_spi_check_status(lo_name, addr, write=True) + + ########################################################################### + # LEDs + ########################################################################### + def set_leds(self, channel, rx, trx_rx, trx_tx): + """ Set the frontpanel LEDs """ + assert channel in (0, 1) + + self.regs.save_state() + if channel == 0: + # ensure to be in SW controlled mode + self.regs.RF0_OPTION = self.regs.RF0_OPTION.RF0_OPTION_SW_DEFINED + self.regs.SW_RF0_CONFIG = 0 + self.regs.RX0_RX_LED[0] = self.regs.RX0_RX_LED[0].RX0_RX_LED_ENABLE \ + if bool(rx) else self.regs.RX0_RX_LED[0].RX0_RX_LED_DISABLE + self.regs.RX0_TRX_LED[0] = self.regs.RX0_TRX_LED[0].RX0_TRX_LED_ENABLE \ + if bool(trx_rx) else self.regs.RX0_TRX_LED[0].RX0_TRX_LED_DISABLE + self.regs.TX0_TRX_LED[0] = self.regs.TX0_TRX_LED[0].TX0_TRX_LED_ENABLE \ + if bool(trx_tx) else self.regs.TX0_TRX_LED[0].TX0_TRX_LED_DISABLE + else: + # ensure to be in SW controlled mode + self.regs.RF1_OPTION = self.regs.RF1_OPTION.RF1_OPTION_SW_DEFINED + self.regs.SW_RF1_CONFIG = 0 + self.regs.RX1_RX_LED[0] = self.regs.RX1_RX_LED[0].RX1_RX_LED_ENABLE \ + if bool(rx) else self.regs.RX1_RX_LED[0].RX1_RX_LED_DISABLE + self.regs.RX1_TRX_LED[0] = self.regs.RX1_TRX_LED[0].RX1_TRX_LED_ENABLE \ + if bool(trx_rx) else self.regs.RX1_TRX_LED[0].RX1_TRX_LED_DISABLE + self.regs.TX1_TRX_LED[0] = self.regs.TX1_TRX_LED[0].TX1_TRX_LED_ENABLE \ + if bool(trx_tx) else self.regs.TX1_TRX_LED[0].TX1_TRX_LED_DISABLE + + for addr in self.regs.get_changed_addrs(): + self.poke_cpld(addr, self.regs.get_reg(addr)) + + ########################################################################### + # Sensors + ########################################################################### + def get_rf_temp_sensor(self, _): + """ + Return the RF temperature sensor value + """ + self.log.trace("Reading RF daughterboard temperature.") + sensor_names = [ + f"TMP112 DB{self.slot_idx} Top", + f"TMP112 DB{self.slot_idx} Bottom", + ] + return get_temp_sensor(sensor_names, log=self.log) diff --git a/mpm/python/usrp_mpm/dboard_manager/zbx_update_cpld.py b/mpm/python/usrp_mpm/dboard_manager/zbx_update_cpld.py new file mode 100644 index 000000000..851cfe997 --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/zbx_update_cpld.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Update the CPLD image for a ZBX daughterboard +""" + +import sys +import os +import argparse +import subprocess +import pyudev +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.mpmutils import check_fpga_state +from usrp_mpm.sys_utils.sysfs_gpio import GPIOBank +from usrp_mpm.periph_manager.x4xx_periphs import CtrlportRegs +from usrp_mpm.periph_manager.x4xx_mb_cpld import MboardCPLD +from usrp_mpm.chips.max10_cpld_flash_ctrl import Max10CpldFlashCtrl +from usrp_mpm.sys_utils.udev import dt_symbol_get_spidev + +OPENOCD_DIR = "/usr/share/openocd/scripts" +CONFIGS = { + 'axi_bitq' : { + 'files' : ["fpga/altera-10m50.cfg"], + 'cmd' : ["interface axi_bitq; axi_bitq_config %u %u %u; adapter_khz %u", + "init; svf -tap 10m50.tap %s -progress -quiet;exit"] + } +} + +AXI_BITQ_ADAPTER_SPEED = 5000 +AXI_BITQ_BUS_CLK = 50000000 + +#The offsets are for JTAG_DB0 and JTAG_DB1 on the motherboard CPLD +DAUGHTERBOARD0_OFFSET = CtrlportRegs.MB_PL_CPLD + 0x60 +DAUGHTERBOARD1_OFFSET = CtrlportRegs.MB_PL_CPLD + 0x80 + +# ZBX flash reconfiguration engine specific offsets +RECONFIG_ENGINE_OFFSET = 0x20 +CPLD_MIN_REVISION = 0x20052016 + +def check_openocd_files(files, logger=None): + """ + Check if all file required by OpenOCD exist + :param logger: logger object + """ + for ocd_file in files: + if not os.path.exists(os.path.join(OPENOCD_DIR, ocd_file)): + if logger is not None: + logger.error("Missing file %s" % os.path.join(OPENOCD_DIR, ocd_file)) + return False + return True + +def find_offset(dboard): + """ + Find the AXI Bitq UIO device + :param dboard: the dboard, can be either 0 or 1 + """ + assert dboard in (0, 1) + return DAUGHTERBOARD0_OFFSET if dboard == 0 else DAUGHTERBOARD1_OFFSET + +def find_axi_bitq_uio(): + """ + Find the AXI Bitq UIO device + """ + label = 'ctrlport-mboard-regs' + + logger = get_logger('update_cpld') + + try: + context = pyudev.Context() + for uio in context.list_devices(subsystem="uio"): + uio_label = uio.attributes.asstring('maps/map0/name') + logger.trace("UIO label: {}, match: {} number: {}".format( + uio_label, uio_label == label, uio.sys_number)) + if uio_label == label: + return int(uio.sys_number) + return None + except OSError as ex: + logger.error("Error while looking for axi_bitq uio nodes: {}".format(ex)) + return None + +def do_update_cpld(filename, daughterboards, updater_mode): + """ + Carry out update process for the CPLD + :param filename: path (on device) to the new CPLD image + :param daughterboards: iterable containing dboard numbers to update + :param updater_mode: the updater method to use- Either flash or legacy (JTAG) + :return: True on success, False otherwise + """ + assert updater_mode in ('flash', 'legacy'), \ + f"Invalid updater method {updater_mode} given" + logger = get_logger('update_cpld') + logger.info("Programming CPLD of dboards {} with image {} using {} mode" + .format(daughterboards, filename, updater_mode)) + + if not daughterboards: + logger.error("Invalid daughterboard selection.") + return False + + if not os.path.exists(filename): + logger.error("CPLD image file {} not found".format(filename)) + return False + + if not check_fpga_state(logger=logger): + logger.error("CPLD lines are routed through fabric, FPGA is not programmed, giving up") + return False + + if updater_mode == 'legacy': + return jtag_cpld_update(filename, daughterboards, logger) + # updater_mode == flash: + for dboard in daughterboards: + dboard = int(dboard, 10) + logger.info("Updating daughterboard slot {}...".format(dboard)) + # enable required daughterboard clock + cpld_spi_node = dt_symbol_get_spidev('mb_cpld') + cpld_control = MboardCPLD(cpld_spi_node, logger) + cpld_control.enable_daughterboard_support_clock(dboard, enable=True) + # setup flash configuration engine and required register access + label = "ctrlport-mboard-regs" + ctrlport_regs = CtrlportRegs(label, logger) + regs = ctrlport_regs.get_db_cpld_iface(dboard) + flash_control = Max10CpldFlashCtrl( + logger, regs, RECONFIG_ENGINE_OFFSET, CPLD_MIN_REVISION) + success = flash_control.update(filename) + # disable clock + cpld_control.enable_daughterboard_support_clock(dboard, enable=False) + if not success: + return success + return True + +def jtag_cpld_update(filename, daughterboards, logger=None): + """ + Carry out update process for the CPLD + :param filename: path (on device) to the new CPLD image + :param daughterboards: iterable containing dboard numbers to update + :return: True on success, False otherwise + """ + mode = 'axi_bitq' + config = CONFIGS[mode] + + if check_openocd_files(config['files'], logger=logger): + logger.trace("Found required OpenOCD files.") + else: + # check_openocd_files logs errors + return False + + for dboard in daughterboards: + logger.info("Updating daughterboard slot {}...".format(dboard)) + + uio_id = find_axi_bitq_uio() + offset = find_offset(int(dboard, 10)) + if uio_id is None or uio_id < 0: + logger.error('Failed to find axi_bitq uio devices. '\ + 'Make sure overlays are up to date') + return False + + cmd = [ + "openocd", + "-c", config['cmd'][0] % (uio_id, AXI_BITQ_BUS_CLK, offset, AXI_BITQ_ADAPTER_SPEED), + "-f", (config['files'][0]).strip(), + "-c", config['cmd'][1] % filename] + + logger.trace("Update CPLD CMD: {}".format(" ".join(cmd))) + subprocess.call(cmd) + + logger.trace("Done programming CPLD...") + return True + +def main(): + """ + Go, go, go! + """ + # Do some setup + def parse_args(): + """Parse the command-line arguments""" + parser = argparse.ArgumentParser(description='Update the CPLD image on ZBX daughterboard') + parser.add_argument("--file", help="Filename of CPLD image", + default="/lib/firmware/ni/cpld-zbx.rpd") + parser.add_argument("--dboards", help="Slot name to program", default="0,1") + parser.add_argument("--updater", + help="The image updater method to use, either " + " 'legacy' (uses openocd) or 'flash'", + default="flash") + parser.add_argument( + '-v', + '--verbose', + help="Increase verbosity level", + action="count", + default=1 + ) + parser.add_argument( + '-q', + '--quiet', + help="Decrease verbosity level", + action="count", + default=0 + ) + return parser.parse_args() + + args = parse_args() + + # We need to make a logger if we're running stand-alone + from usrp_mpm.mpmlog import get_main_logger + log = get_main_logger(log_default_delta=args.verbose-args.quiet) + + dboards = args.dboards.split(",") + if any([x not in ('0', '1') for x in dboards]): + log.error("Unsupported dboards requested: %s", dboards) + return False + + return do_update_cpld(args.file, dboards, args.updater) + + +if __name__ == "__main__": + sys.exit(not main()) diff --git a/mpm/python/usrp_mpm/mpmutils.py b/mpm/python/usrp_mpm/mpmutils.py index 5da81ecfe..a569c85ad 100644 --- a/mpm/python/usrp_mpm/mpmutils.py +++ b/mpm/python/usrp_mpm/mpmutils.py @@ -8,6 +8,7 @@ Miscellaneous utilities for MPM """ import time +import pyudev from contextlib import contextmanager def poll_with_timeout(state_check, timeout_ms, interval_ms): @@ -90,18 +91,18 @@ def assert_compat_number( log=None, ): """ - Check if a compat number pair is acceptable. A compat number is a pair of - integers (MAJOR, MINOR). A compat number is not acceptable if the major + Check if a compat number tuple is acceptable. A compat number is a tuple of + integers (MAJOR, MINOR, BUILD). A compat number is not acceptable if the major part differs from the expected value (regardless of how it's different) or if the minor part is behind the expected value and fail_on_old_minor was - given. + given. Build number is not checked here. On failure, will throw a RuntimeError. Arguments: - expected_compat -- A tuple (major, minor) which represents the compat - number we are expecting. - actual_compat -- A tuple (major, minor) which represents the compat number - that is actually available. + expected_compat -- A tuple (major, minor) or (major, minor, build) which + represents the compat number we are expecting. + actual_compat -- A tuple (major, minor) or (major, minor, build) which + represents the compat number that is actually available. component -- A name of the component for which we are checking the compat number, e.g. "FPGA". fail_on_old_minor -- Will also fail if the actual minor compat number is @@ -110,15 +111,20 @@ def assert_compat_number( log -- Logger object. If given, will use this to report on intermediate steps and non-fatal minor compat mismatches. """ - assert len(expected_compat) == 2 - assert len(actual_compat) == 2 + valid_tuple_lengths = (2, 3) + assert len(expected_compat) in valid_tuple_lengths, ( + f"Version {expected_compat} has invalid format. Valid formats are" + "(major, minor) or (major, minor, build)") + assert len(actual_compat) in valid_tuple_lengths, ( + f"Version {expected_compat} has invalid format. Valid formats are" + "(major, minor) or (major, minor, build)") log_err = lambda msg: log.error(msg) if log is not None else None log_warn = lambda msg: log.warning(msg) if log is not None else None expected_actual_str = "Expected: {:d}.{:d} Actual: {:d}.{:d}".format( expected_compat[0], expected_compat[1], actual_compat[0], actual_compat[1], ) - component_str = "" if component is None else " for component `{}'".format( + component_str = "" if component is None else " for component '{}'".format( component ) if actual_compat[0] != expected_compat[0]: @@ -128,7 +134,7 @@ def assert_compat_number( log_err(err_msg) raise RuntimeError(err_msg) if actual_compat[1] > expected_compat[1]: - log_warn("Actual minor compat ahead of expected compat{}. {}".format( + log_warn("Minor compat ahead of expected compat{}. {}".format( component_str, expected_actual_str )) if actual_compat[1] < expected_compat[1]: @@ -139,7 +145,6 @@ def assert_compat_number( log_err(err_msg) raise RuntimeError(err_msg) log_warn(err_msg) - return def str2bool(value): """Return a Boolean value from a string, even if the string is not simply @@ -188,3 +193,21 @@ def lock_guard(lockable): finally: lockable.unlock() +def check_fpga_state(which=0, logger=None): + """ + Check if the FPGA is operational + :param which: the FPGA to check + """ + try: + context = pyudev.Context() + fpga_mgrs = list(context.list_devices(subsystem="fpga_manager")) + if fpga_mgrs: + state = fpga_mgrs[which].attributes.asstring('state') + if logger is not None: + logger.trace("FPGA State: {}".format(state)) + return state == "operating" + return False + except OSError as ex: + if logger is not None: + logger.error("Error while checking FPGA status: {}".format(ex)) + return False diff --git a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt index 747b8967a..1ca96f53e 100644 --- a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt +++ b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2017-2018 Ettus Research, a National Instruments Company +# Copyright 2017-2019 Ettus Research, a National Instruments Company # # SPDX-License-Identifier: GPL-3.0-or-later # @@ -18,6 +18,17 @@ set(USRP_MPM_PERIPHMGR_FILES ${CMAKE_CURRENT_SOURCE_DIR}/e320_periphs.py ${CMAKE_CURRENT_SOURCE_DIR}/e31x.py ${CMAKE_CURRENT_SOURCE_DIR}/e31x_periphs.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_periphs.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_clk_aux.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_clk_mgr.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_sample_pll.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_reference_pll.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_update_cpld.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_gps_mgr.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_mb_cpld.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_rfdc_regs.py + ${CMAKE_CURRENT_SOURCE_DIR}/x4xx_rfdc_ctrl.py ${CMAKE_CURRENT_SOURCE_DIR}/sim.py ) list(APPEND USRP_MPM_FILES ${USRP_MPM_PERIPHMGR_FILES}) diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py index 53426a615..35986a83c 100644 --- a/mpm/python/usrp_mpm/periph_manager/base.py +++ b/mpm/python/usrp_mpm/periph_manager/base.py @@ -170,6 +170,8 @@ class PeriphManagerBase(object): dboard_eeprom_symbols = "db[0,1]_eeprom" # symbol glob fox auxiliary boards auxboard_eeprom_symbols = "*aux_eeprom" + # List of discoverable features supported by a device. + discoverable_features = [] # Disable checks for unused args in the overridables, because the default @@ -504,7 +506,18 @@ class PeriphManagerBase(object): for name, path in eeprom_paths.items(): self.log.debug("Reading EEPROM info for %s...", name) if not path: - self.log.debug("Not present. Skipping board") + if "db" in name: + # In order to support having a single dboard in slot 1 + # with slot 0 empty on a x4xx, we pretend that there is + # a dummy "EmptyDaughterboard" here. + self.log.debug("Not present. Inserting dummy DB info") + result[name] = { + 'eeprom_md': {'serial': 'deadbee', 'pid': 0x0}, + 'eeprom_raw': [], + 'pid': 0x0 + } + else: + self.log.debug("Not present. Skipping board") continue try: eeprom_md, eeprom_rawdata = self._read_dboard_eeprom_data(path) @@ -743,7 +756,8 @@ class PeriphManagerBase(object): "Cannot run deinit(), device was never fully initialized!") return self.log.trace("Mboard deinit() called.") - for dboard in self.dboards: + for slot, dboard in enumerate(self.dboards): + self.log.trace("call deinit() on dBoard in slot {}".format(slot)) dboard.deinit() def tear_down(self): @@ -752,6 +766,8 @@ class PeriphManagerBase(object): deconstruction. """ self.log.trace("Teardown called for Peripheral Manager base.") + for each in self.dboards: + each.tear_down() ########################################################################### # RFNoC & Device Info @@ -895,6 +911,7 @@ class PeriphManagerBase(object): assert (len(metadata_l) == len(data_l)),\ "update_component arguments must be the same length" # Iterate through the components, updating each in turn + basepath = os.path.join(os.sep, "tmp", "uploads") for metadata, data in zip(metadata_l, data_l): id_str = metadata['id'] filename = os.path.basename(metadata['filename']) @@ -903,7 +920,7 @@ class PeriphManagerBase(object): id_str, self.updateable_components.keys() )) raise KeyError("Update component not implemented for {}".format(id_str)) - self.log.trace("Updating component: {}".format(id_str)) + self.log.trace("Downloading component: {}".format(id_str)) if 'md5' in metadata: given_hash = metadata['md5'] comp_hash = md5() @@ -920,10 +937,9 @@ class PeriphManagerBase(object): comp_hash, given_hash)) raise RuntimeError("Component file hash mismatch") else: - self.log.trace("Loading unverified {} image.".format( + self.log.trace("Downloading unhashed {} image.".format( id_str )) - basepath = os.path.join(os.sep, "tmp", "uploads") filepath = os.path.join(basepath, filename) if not os.path.isdir(basepath): self.log.trace("Creating directory {}".format(basepath)) @@ -931,9 +947,15 @@ class PeriphManagerBase(object): self.log.trace("Writing data to {}".format(filepath)) with open(filepath, 'wb') as comp_file: comp_file.write(data) + + # do the actual installation on the device + for metadata in metadata_l: + id_str = metadata['id'] + filename = os.path.basename(metadata['filename']) + filepath = os.path.join(basepath, filename) update_func = \ getattr(self, self.updateable_components[id_str]['callback']) - self.log.info("Updating component `%s'", id_str) + self.log.info("Installing component `%s'", id_str) update_func(filepath, metadata) return True @@ -950,7 +972,7 @@ class PeriphManagerBase(object): self.log.trace("Component info: {}".format(metadata)) # Convert all values to str return dict([a, str(x)] for a, x in metadata.items()) - # else: + self.log.trace("Component not found in updateable components: {}" .format(component_name)) return {} @@ -1202,3 +1224,21 @@ class PeriphManagerBase(object): "time_source": self.get_time_source(), "clock_source": self.get_clock_source(), } + + ########################################################################### + # Clock/Time API + ########################################################################### + def set_clock_source_out(self, enable=True): + """ + Allows routing the clock configured as source to the RefOut terminal. + """ + raise NotImplementedError("set_clock_source_out() not implemented.") + + ####################################################################### + # Discoverable Features + ####################################################################### + def supports_feature(self, query): + """ + Returns true if the queried feature is supported by a device. + """ + return query in self.discoverable_features diff --git a/mpm/python/usrp_mpm/periph_manager/common.py b/mpm/python/usrp_mpm/periph_manager/common.py index e09be835e..6ed7f6c53 100644 --- a/mpm/python/usrp_mpm/periph_manager/common.py +++ b/mpm/python/usrp_mpm/periph_manager/common.py @@ -10,7 +10,7 @@ Common code for all MPM devices import datetime from usrp_mpm.sys_utils.uio import UIO -class MboardRegsCommon(object): +class MboardRegsCommon: """ Parent class for mboard regs that are common between *all* MPM devices """ @@ -34,6 +34,10 @@ class MboardRegsCommon(object): MB_TIME_BASE_PERIOD_LO = 0x101C MB_TIME_BASE_PERIOD_HI = 0x1020 MB_TIMEKEEPER_OFFSET = 12 + # Timekeeper control words + MB_TIME_SET_NOW = 0x0001 + MB_TIME_SET_NEXT_PPS = 0x0002 + MB_TIME_SET_NEXT_SYNC = 0x0004 # Bitfield locations for the MB_RFNOC_INFO register. MB_RFNOC_INFO_PROTO_VER = 0 MB_RFNOC_INFO_CHDR_WIDTH = 16 @@ -179,12 +183,13 @@ class MboardRegsCommon(object): """ addr_lo = \ self.MB_TIME_EVENT_LO + tk_idx * self.MB_TIMEKEEPER_OFFSET - addr_hi = addr_lo + 4 + addr_hi = \ + self.MB_TIME_EVENT_HI + tk_idx * self.MB_TIMEKEEPER_OFFSET addr_ctrl = \ self.MB_TIME_CTRL + tk_idx * self.MB_TIMEKEEPER_OFFSET time_lo = ticks & 0xFFFFFFFF time_hi = (ticks >> 32) & 0xFFFFFFFF - time_ctrl = 0x2 if next_pps else 0x1 + time_ctrl = self.MB_TIME_SET_NEXT_PPS if next_pps else self.MB_TIME_SET_NOW self.log.trace("Setting time on timekeeper %d to %d %s", tk_idx, ticks, ("on next pps" if next_pps else "now")) with self.regs: diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx.py b/mpm/python/usrp_mpm/periph_manager/x4xx.py new file mode 100644 index 000000000..b32cbeb08 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx.py @@ -0,0 +1,1280 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +X400 implementation module +""" + +import threading +import copy +from time import sleep +from os import path +from collections import namedtuple +from pyudev import DeviceNotFoundByNameError +from usrp_mpm import lib # Pulls in everything from C++-land +from usrp_mpm import tlv_eeprom +from usrp_mpm.cores import WhiteRabbitRegsControl +from usrp_mpm.components import ZynqComponents +from usrp_mpm.sys_utils import dtoverlay +from usrp_mpm.sys_utils import ectool +from usrp_mpm.sys_utils import i2c_dev +from usrp_mpm.sys_utils.gpio import Gpio +from usrp_mpm.sys_utils.udev import dt_symbol_get_spidev +from usrp_mpm.rpc_server import no_claim, no_rpc +from usrp_mpm.mpmutils import assert_compat_number, poll_with_timeout +from usrp_mpm.periph_manager import PeriphManagerBase +from usrp_mpm.xports import XportMgrUDP +from usrp_mpm.periph_manager.x4xx_periphs import MboardRegsControl +from usrp_mpm.periph_manager.x4xx_periphs import CtrlportRegs +from usrp_mpm.periph_manager.x4xx_periphs import DioControl +from usrp_mpm.periph_manager.x4xx_periphs import QSFPModule +from usrp_mpm.periph_manager.x4xx_periphs import get_temp_sensor +from usrp_mpm.periph_manager.x4xx_mb_cpld import MboardCPLD +from usrp_mpm.periph_manager.x4xx_clk_aux import ClockingAuxBrdControl +from usrp_mpm.periph_manager.x4xx_clk_mgr import X4xxClockMgr +from usrp_mpm.periph_manager.x4xx_gps_mgr import X4xxGPSMgr +from usrp_mpm.periph_manager.x4xx_rfdc_ctrl import X4xxRfdcCtrl +from usrp_mpm.dboard_manager.x4xx_db_iface import X4xxDboardIface +from usrp_mpm.dboard_manager.zbx import ZBX + + +X400_DEFAULT_EXT_CLOCK_FREQ = 10e6 +X400_DEFAULT_MASTER_CLOCK_RATE = 122.88e6 +X400_DEFAULT_TIME_SOURCE = X4xxClockMgr.TIME_SOURCE_INTERNAL +X400_DEFAULT_CLOCK_SOURCE = X4xxClockMgr.CLOCK_SOURCE_INTERNAL +X400_DEFAULT_ENABLE_PPS_EXPORT = True +X400_FPGA_COMPAT = (7, 2) +X400_DEFAULT_TRIG_DIRECTION = ClockingAuxBrdControl.DIRECTION_OUTPUT +X400_MONITOR_THREAD_INTERVAL = 1.0 # seconds +QSFPModuleConfig = namedtuple("QSFPModuleConfig", "modprs modsel devsymbol") +X400_QSFP_I2C_CONFIGS = [ + QSFPModuleConfig(modprs='QSFP0_MODPRS', modsel='QSFP0_MODSEL_n', devsymbol='qsfp0_i2c'), + QSFPModuleConfig(modprs='QSFP1_MODPRS', modsel='QSFP1_MODSEL_n', devsymbol='qsfp1_i2c')] +RPU_SUCCESS_REPORT = 'Success' +RPU_FAILURE_REPORT = 'Failure' +RPU_REMOTEPROC_FIRMWARE_PATH = '/lib/firmware' +RPU_REMOTEPROC_PREFIX_PATH = '/sys/class/remoteproc/remoteproc' +RPU_REMOTEPROC_PROPERTY_FIRMWARE = 'firmware' +RPU_REMOTEPROC_PROPERTY_STATE = 'state' +RPU_STATE_COMMAND_START = 'start' +RPU_STATE_COMMAND_STOP = 'stop' +RPU_STATE_OFFLINE = 'offline' +RPU_STATE_RUNNING = 'running' +RPU_MAX_FIRMWARE_SIZE = 0x100000 +RPU_MAX_STATE_CHANGE_TIME_IN_MS = 10000 +RPU_STATE_CHANGE_POLLING_INTERVAL_IN_MS = 100 + +DIOAUX_EEPROM = "dioaux_eeprom" +DIOAUX_PID = 0x4003 + +# pylint: disable=too-few-public-methods +class EepromTagMap: + """ + Defines the tagmap for EEPROMs matching this magic. + The tagmap is a dictionary mapping an 8-bit tag to a NamedStruct instance. + The canonical list of tags and the binary layout of the associated structs + is defined in mpm/tools/tlv_eeprom/usrp_eeprom.h. Only the subset relevant + to MPM are included below. + """ + magic = 0x55535250 + tagmap = { + # 0x10: usrp_eeprom_board_info + 0x10: tlv_eeprom.NamedStruct('< H H H 7s 1x', + ['pid', 'rev', 'rev_compat', 'serial']), + # 0x11: usrp_eeprom_module_info + 0x11: tlv_eeprom.NamedStruct('< H H 7s 1x', + ['module_pid', 'module_rev', 'module_serial']), + } + + +############################################################################### +# Transport managers +############################################################################### +class X400XportMgrUDP(XportMgrUDP): + "X400-specific UDP configuration" + iface_config = { + 'sfp0': { + 'label': 'misc-enet-regs0', + 'type': 'sfp', + }, + 'sfp0_1': { + 'label': 'misc-enet-regs0-1', + 'type': 'sfp', + }, + 'sfp0_2': { + 'label': 'misc-enet-regs0-2', + 'type': 'sfp', + }, + 'sfp0_3': { + 'label': 'misc-enet-regs0-3', + 'type': 'sfp', + }, + 'sfp1': { + 'label': 'misc-enet-regs1', + 'type': 'sfp', + }, + 'sfp1_1': { + 'label': 'misc-enet-regs1-1', + 'type': 'sfp', + }, + 'sfp1_2': { + 'label': 'misc-enet-regs1-2', + 'type': 'sfp', + }, + 'sfp1_3': { + 'label': 'misc-enet-regs1-3', + 'type': 'sfp', + }, + 'int0': { + 'label': 'misc-enet-int-regs', + 'type': 'internal', + }, + 'eth0': { + 'label': '', + 'type': 'forward', + } + } +# pylint: enable=too-few-public-methods + + +############################################################################### +# Main Class +############################################################################### +class x4xx(ZynqComponents, PeriphManagerBase): + """ + Holds X400 specific attributes and methods + """ + ######################################################################### + # Overridables + # + # See PeriphManagerBase for documentation on these fields. We try and keep + # them in the same order as they are in PeriphManagerBase for easier lookup. + ######################################################################### + pids = {0x0410: 'x410'} + description = "X400-Series Device" + eeprom_search = PeriphManagerBase._EepromSearch.SYMBOL + # This is not in the overridables section from PeriphManagerBase, but we use + # it below + eeprom_magic = EepromTagMap.magic + mboard_eeprom_offset = 0 + mboard_eeprom_max_len = 256 + mboard_eeprom_magic = eeprom_magic + mboard_info = {"type": "x4xx"} + mboard_max_rev = 5 # RevE + max_num_dboards = 2 + mboard_sensor_callback_map = { + # List of motherboard sensors that are always available. There are also + # GPS sensors, but they get added during __init__() only when there is + # a GPS available. + 'ref_locked': 'get_ref_lock_sensor', + 'fan0': 'get_fan0_sensor', + 'fan1': 'get_fan1_sensor', + 'temp_fpga' : 'get_fpga_temp_sensor', + 'temp_internal' : 'get_internal_temp_sensor', + 'temp_main_power' : 'get_main_power_temp_sensor', + 'temp_scu_internal' : 'get_scu_internal_temp_sensor', + } + db_iface = X4xxDboardIface + dboard_eeprom_magic = eeprom_magic + updateable_components = { + 'fpga': { + 'callback': "update_fpga", + 'path': '/lib/firmware/{}.bin', + 'reset': True, + 'check_dts_for_compatibility': True, + 'compatibility': { + 'fpga': { + 'current': X400_FPGA_COMPAT, + 'oldest': (7, 0), + }, + 'cpld_ifc' : { + 'current': (2, 0), + 'oldest': (2, 0), + }, + 'db_gpio_ifc': { + 'current': (1, 0), + 'oldest': (1, 0), + }, + 'rf_core_100m': { + 'current': (1, 0), + 'oldest': (1, 0), + }, + 'rf_core_400m': { + 'current': (1, 0), + 'oldest': (1, 0), + }, + } + }, + 'dts': { + 'callback': "update_dts", + 'path': '/lib/firmware/{}.dts', + 'output': '/lib/firmware/{}.dtbo', + 'reset': False, + }, + } + discoverable_features = ["ref_clk_calibration", "time_export"] + # + # End of overridables from PeriphManagerBase + ########################################################################### + + + # X400-specific settings + # Label for the mboard UIO + mboard_regs_label = "mboard-regs" + ctrlport_regs_label = "ctrlport-mboard-regs" + # Label for the white rabbit UIO + wr_regs_label = "wr-regs" + # Override the list of updateable components + # X4xx specific discoverable features + + @classmethod + def generate_device_info(cls, eeprom_md, mboard_info, dboard_infos): + """ + Hard-code our product map + """ + # Add the default PeriphManagerBase information first + device_info = super().generate_device_info( + eeprom_md, mboard_info, dboard_infos) + # Then add X4xx-specific information + mb_pid = eeprom_md.get('pid') + device_info['product'] = cls.pids.get(mb_pid, 'unknown') + module_serial = eeprom_md.get('module_serial') + if module_serial is not None: + device_info['serial'] = module_serial + return device_info + + @staticmethod + def list_required_dt_overlays(device_info): + """ + Lists device tree overlays that need to be applied before this class can + be used. List of strings. + Are applied in order. + + eeprom_md -- Dictionary of info read out from the mboard EEPROM + device_args -- Arbitrary dictionary of info, typically user-defined + """ + return [device_info['product']] + + def _init_mboard_overlays(self): + """ + Load all required overlays for this motherboard + Overriden from the base implementation to force apply even if + the overlay was already loaded. + """ + requested_overlays = self.list_required_dt_overlays( + self.device_info, + ) + self.log.debug("Motherboard requests device tree overlays: {}".format( + requested_overlays + )) + # Remove all overlays before applying new ones + for overlay in requested_overlays: + dtoverlay.rm_overlay_safe(overlay) + for overlay in requested_overlays: + dtoverlay.apply_overlay_safe(overlay) + # Need to wait here a second to make sure the ethernet interfaces are up + # TODO: Fine-tune this number, or wait for some smarter signal. + sleep(1) + + ########################################################################### + # Ctor and device initialization tasks + ########################################################################### + def __init__(self, args): + super(x4xx, self).__init__() + + self._tear_down = False + self._rpu_initialized = False + self._status_monitor_thread = None + self._master_clock_rate = None + self._gps_mgr = None + self._clk_mgr = None + self._safe_sync_source = { + 'clock_source': X400_DEFAULT_CLOCK_SOURCE, + 'time_source': X400_DEFAULT_TIME_SOURCE, + } + self.rfdc = None + self.mboard_regs_control = None + self.ctrlport_regs = None + self.cpld_control = None + self.dio_control = None + try: + self._init_peripherals(args) + self.init_dboards(args) + self._clk_mgr.set_dboard_reset_cb( + lambda enable: [db.reset_clock(enable) for db in self.dboards]) + except Exception as ex: + self.log.error("Failed to initialize motherboard: %s", str(ex), exc_info=ex) + self._initialization_status = str(ex) + self._device_initialized = False + if not self._device_initialized: + # Don't try and figure out what's going on. Just give up. + return + try: + if not args.get('skip_boot_init', False): + self.init(args) + except Exception as ex: + self.log.warning("Failed to initialize device on boot: %s", str(ex)) + + # The parent class versions of these functions require access to self, but + # these versions don't. + # pylint: disable=no-self-use + def _read_mboard_eeprom_data(self, eeprom_path): + """ Returns a tuple (eeprom_dict, eeprom_rawdata) for the motherboard + EEPROM. + """ + return tlv_eeprom.read_eeprom(eeprom_path, EepromTagMap.tagmap, + EepromTagMap.magic, None) + + def _read_dboard_eeprom_data(self, eeprom_path): + """ Returns a tuple (eeprom_dict, eeprom_rawdata) for a daughterboard + EEPROM. + """ + return tlv_eeprom.read_eeprom(eeprom_path, EepromTagMap.tagmap, + EepromTagMap.magic, None) + # pylint: enable=no-self-use + + def _check_fpga_compat(self): + " Throw an exception if the compat numbers don't match up " + actual_compat = self.mboard_regs_control.get_compat_number() + self.log.debug("Actual FPGA compat number: {:d}.{:d}".format( + actual_compat[0], actual_compat[1] + )) + assert_compat_number( + X400_FPGA_COMPAT, + actual_compat, + component="FPGA", + fail_on_old_minor=False, + log=self.log + ) + + def _init_gps_mgr(self): + """ + Initialize the GPS manager and the sensors. + Note that mpmd_impl queries all available sensors at initialization + time, in order to populate the property tree. That means we can't + dynamically load/unload sensors. Instead, we have to make sure that + the sensors can handle the GPS sensors, even when it's disabled. That + is pushed into the GPS manager class. + """ + self.log.debug("Found GPS, adding sensors.") + gps_mgr = X4xxGPSMgr(self._clocking_auxbrd, self.log) + # We can't use _add_public_methods(), because we only want a subset of + # the public methods. Also, we want to know which sensors were added so + # we can also add them to mboard_sensor_callback_map. + new_methods = gps_mgr.extend(self) + self.mboard_sensor_callback_map.update(new_methods) + return gps_mgr + + def _monitor_status(self): + """ + Status monitoring thread: This should be executed in a thread. It will + continuously monitor status of the following peripherals: + + - REF lock (update back-panel REF LED) + """ + self.log.trace("Launching monitor loop...") + cond = threading.Condition() + cond.acquire() + while not self._tear_down: + ref_locked = self.get_ref_lock_sensor()['value'] == 'true' + if self._clocking_auxbrd is not None: + self._clocking_auxbrd.set_ref_lock_led(ref_locked) + # Now wait + if cond.wait_for( + lambda: self._tear_down, + X400_MONITOR_THREAD_INTERVAL): + break + cond.release() + self.log.trace("Terminating monitor loop.") + + def _assert_rfdc_powered(self): + """ + Assert that RFdc power is enabled, throw RuntimeError otherwise. + """ + if not self._rfdc_powered.get(): + err_msg = "RFDC is not powered on" + self.log.error(err_msg) + raise RuntimeError(err_msg) + + def _get_serial_number(self): + """ + Read the serial number from eeprom, falling back to the board S/N + if the module S/N is not populated. + """ + serial_number = self._eeprom_head.get("module_serial") + if serial_number is None: + self.log.warning( + 'Module serial number not programmed, falling back to motherboard serial') + serial_number = self._eeprom_head["serial"] + return serial_number.rstrip(b'\x00') + + def _init_peripherals(self, args): + """ + Turn on all peripherals. This may throw an error on failure, so make + sure to catch it. + """ + # Sanity checks + assert self.mboard_info.get('product') in self.pids.values(), \ + "Device product could not be determined!" + # Init peripherals + self._rfdc_powered = Gpio('RFDC_POWERED', Gpio.INPUT) + # Init RPU Manager + self.log.trace("Initializing RPU manager peripheral...") + self.init_rpu() + # Init clocking aux board + self.log.trace("Initializing Clocking Aux Board controls...") + has_gps = False + try: + self._clocking_auxbrd = ClockingAuxBrdControl() + self.log.trace("Initialized Clocking Aux Board controls") + has_gps = self._clocking_auxbrd.is_gps_supported() + except RuntimeError: + self.log.warning( + "GPIO I2C bus could not be found for the Clocking Aux Board, " + "disabling Clocking Aux Board functionality.") + self._clocking_auxbrd = None + self._safe_sync_source = { + 'clock_source': X4xxClockMgr.CLOCK_SOURCE_MBOARD, + 'time_source': X4xxClockMgr.TIME_SOURCE_INTERNAL, + } + + initial_clock_source = args.get('clock_source', X400_DEFAULT_CLOCK_SOURCE) + if self._clocking_auxbrd: + self._add_public_methods(self._clocking_auxbrd, "clkaux") + else: + initial_clock_source = X4xxClockMgr.CLOCK_SOURCE_MBOARD + + # Init CPLD before talking to clocking ICs + cpld_spi_node = dt_symbol_get_spidev('mb_cpld') + self.cpld_control = MboardCPLD(cpld_spi_node, self.log) + self.cpld_control.check_signature() + self.cpld_control.check_compat_version() + self.cpld_control.trace_git_hash() + + self._assert_rfdc_powered() + # Init clocking after CPLD as the SPLL communication is relying on it. + # We try and guess the correct master clock rate here based on defaults + # and args. Since we are still in __init__(), the args that come from mpm.conf + # are empty. We can't detect the real default MCR, because we need + # the RFDC controls for that -- but they won't work without clocks. So + # let's pick a sensible default MCR value, init the clocks, and fix the + # MCR value further down. + self._master_clock_rate = float( + args.get('master_clock_rate', X400_DEFAULT_MASTER_CLOCK_RATE)) + sample_clock_freq, _, is_legacy_mode, _ = \ + X4xxRfdcCtrl.master_to_sample_clk[self._master_clock_rate] + self._clk_mgr = X4xxClockMgr( + initial_clock_source, + time_source=args.get('time_source', X400_DEFAULT_TIME_SOURCE), + ref_clock_freq=float(args.get( + 'ext_clock_freq', X400_DEFAULT_EXT_CLOCK_FREQ)), + sample_clock_freq=sample_clock_freq, + is_legacy_mode=is_legacy_mode, + clk_aux_board=self._clocking_auxbrd, + cpld_control=self.cpld_control, + log=self.log) + self._add_public_methods( + self._clk_mgr, + prefix="", + filter_cb=lambda name, method: not hasattr(method, '_norpc') + ) + + # Overlay must be applied after clocks have been configured + self.overlay_apply() + + # Init Mboard Regs + self.log.trace("Initializing MBoard reg controls...") + serial_number = self._get_serial_number() + self.mboard_regs_control = MboardRegsControl( + self.mboard_regs_label, self.log) + self._check_fpga_compat() + self.mboard_regs_control.set_serial_number(serial_number) + self.mboard_regs_control.get_git_hash() + self.mboard_regs_control.get_build_timestamp() + self._clk_mgr.mboard_regs_control = self.mboard_regs_control + + # Create control for RFDC + self.rfdc = X4xxRfdcCtrl(self._clk_mgr.get_spll_freq, self.log) + self._add_public_methods( + self.rfdc, prefix="", + filter_cb=lambda name, method: not hasattr(method, '_norpc') + ) + + self._update_fpga_type() + + # Force reset the RFDC to ensure it is in a good state + self.rfdc.set_reset(reset=True) + self.rfdc.set_reset(reset=False) + + # Synchronize SYSREF and clock distributed to all converters + self.rfdc.sync() + self._clk_mgr.set_rfdc_reset_cb(self.rfdc.set_reset) + + # The initial default mcr only works if we have an FPGA with + # a decimation of 2. But we need the overlay applied before we + # can detect decimation, and that requires clocks to be initialized. + self.set_master_clock_rate(self.rfdc.get_default_mcr()) + + # Init ctrlport endpoint + self.ctrlport_regs = CtrlportRegs(self.ctrlport_regs_label, self.log) + + # Init IPass cable status forwarding and CMI + self.cpld_control.set_serial_number(serial_number) + self.cpld_control.set_cmi_device_ready( + self.mboard_regs_control.is_pcie_present()) + # The CMI transmission can be disabled by setting the cable status + # to be not connected. All images except for the LV PCIe variant + # provide a fixed "cables are unconnected" status. The LV PCIe image + # reports the correct status. As the FPGA holds this information it + # is possible to always enable the iPass cable present forwarding. + self.ctrlport_regs.enable_cable_present_forwarding(True) + + # Init DIO + if self._check_compat_aux_board(DIOAUX_EEPROM, DIOAUX_PID): + self.dio_control = DioControl(self.mboard_regs_control, + self.cpld_control, self.log) + # add dio_control public methods to MPM API + self._add_public_methods(self.dio_control, "dio") + + # Init QSFP modules + for idx, config in enumerate(X400_QSFP_I2C_CONFIGS): + attr = QSFPModule( + config.modprs, config.modsel, config.devsymbol, self.log) + setattr(self, "_qsfp_module{}".format(idx), attr) + self._add_public_methods(attr, "qsfp{}".format(idx)) + + # Init GPS + if has_gps: + self._gps_mgr = self._init_gps_mgr() + # Init CHDR transports + self._xport_mgrs = { + 'udp': X400XportMgrUDP(self.log, args), + } + # Spawn status monitoring thread + self.log.trace("Spawning status monitor thread...") + self._status_monitor_thread = threading.Thread( + target=self._monitor_status, + name="X4xxStatusMonitorThread", + daemon=True, + ) + self._status_monitor_thread.start() + # Init complete. + self.log.debug("Device info: {}".format(self.device_info)) + + def _check_compat_aux_board(self, name, pid): + """ + Check whether auxiliary board given by name and pid can be found + :param name: symbol name of the auxiliary board which is used as + lookup for the dictionary of available boards. + :param pid: PID the board must have to be considered compatible + :return True if board is available with matching PID, + False otherwise + """ + assert(isinstance(self._aux_board_infos, dict)), "No EEPROM data" + board_info = self._aux_board_infos.get(name, None) + if board_info is None: + self.log.warning("Board for %s not present" % name) + return False + if board_info.get("pid", 0) != pid: + self.log.error("Expected PID for board %s to be 0x%04x but found " + "0x%04x" % (name, pid, board_info["pid"])) + return False + self.log.debug("Found compatible board for %s " + "(PID: 0x%04x)" % (name, board_info["pid"])) + return True + + def init_rpu(self): + """ + Initializes the RPU image manager + """ + if self._rpu_initialized: + return + + # Check presence/state of RPU cores + try: + for core_number in [0, 1]: + self.log.trace( + "RPU Core %d state: %s", + core_number, + self.get_rpu_state(core_number)) + # TODO [psisterh, 5 Dec 2019] + # Should we force core to + # stop if running or in error state? + self.log.trace("Initialized RPU cores successfully.") + self._rpu_initialized = True + except FileNotFoundError: + self.log.warning( + "Failed to initialize RPU: remoteproc sysfs not present.") + + ########################################################################### + # Session init and deinit + ########################################################################### + def init(self, args): + """ + Calls init() on the parent class, and then programs the Ethernet + dispatchers accordingly. + """ + if not self._device_initialized: + self.log.warning( + "Cannot run init(), device was never fully initialized!") + return False + + # We need to disable the PPS out during clock and dboard initialization in order + # to avoid glitches. + if self._clocking_auxbrd is not None: + self._clocking_auxbrd.set_trig(False) + + # If the caller has not specified clock_source or time_source, set them + # to the values currently configured. + args['clock_source'] = args.get('clock_source', self._clk_mgr.get_clock_source()) + args['time_source'] = args.get('time_source', self._clk_mgr.get_time_source()) + self.set_sync_source(args) + + # If a Master Clock Rate was specified, + # re-configure the Sample PLL and all downstream clocks + if 'master_clock_rate' in args: + self.set_master_clock_rate(float(args['master_clock_rate'])) + + # Initialize CtrlportRegs (manually opens the UIO resource for faster access) + self.ctrlport_regs.init() + + # Note: The parent class takes care of calling init() on all the + # daughterboards + result = super(x4xx, self).init(args) + + # Now the clocks are all enabled, we can also enable PPS export: + if self._clocking_auxbrd is not None: + self._clocking_auxbrd.set_trig( + args.get('pps_export', X400_DEFAULT_ENABLE_PPS_EXPORT), + args.get('trig_direction', X400_DEFAULT_TRIG_DIRECTION) + ) + + for xport_mgr in self._xport_mgrs.values(): + xport_mgr.init(args) + return result + + def deinit(self): + """ + Clean up after a UHD session terminates. + """ + if not self._device_initialized: + self.log.warning( + "Cannot run deinit(), device was never fully initialized!") + return + + if self.get_ref_lock_sensor()['unit'] != 'locked': + self.log.error("ref clocks aren't locked, falling back to default") + source = {"clock_source": X400_DEFAULT_CLOCK_SOURCE, + "time_source": X400_DEFAULT_TIME_SOURCE + } + self.set_sync_source(source) + super(x4xx, self).deinit() + self.ctrlport_regs.deinit() + for xport_mgr in self._xport_mgrs.values(): + xport_mgr.deinit() + + def tear_down(self): + """ + Tear down all members that need to be specially handled before + deconstruction. + For X400, this means the overlay. + """ + self.log.trace("Tearing down X4xx device...") + self._tear_down = True + if self._device_initialized: + self._status_monitor_thread.join(3 * X400_MONITOR_THREAD_INTERVAL) + if self._status_monitor_thread.is_alive(): + self.log.error("Could not terminate monitor thread! " + "This could result in resource leaks.") + # call tear_down on daughterboards first + super(x4xx, self).tear_down() + if self.dio_control is not None: + self.dio_control.tear_down() + self.rfdc.unset_cbs() + self._clk_mgr.unset_cbs() + # remove x4xx overlay + active_overlays = self.list_active_overlays() + self.log.trace("X4xx has active device tree overlays: {}".format( + active_overlays + )) + for overlay in active_overlays: + dtoverlay.rm_overlay(overlay) + + ########################################################################### + # Transport API + ########################################################################### + # pylint: disable=no-self-use + def get_chdr_link_types(self): + """ + This will only ever return a single item (udp). + """ + return ["udp"] + # pylint: enable=no-self-use + + def get_chdr_link_options(self, xport_type): + """ + Returns a list of dictionaries. Every dictionary contains information + about one way to connect to this device in order to initiate CHDR + traffic. + + The interpretation of the return value is very highly dependant on the + transport type (xport_type). + For UDP, the every entry of the list has the following keys: + - ipv4 (IP Address) + - port (UDP port) + - link_rate (bps of the link, e.g. 10e9 for 10GigE) + """ + if xport_type not in self._xport_mgrs: + self.log.warning("Can't get link options for unknown link type: `{}'.") + return [] + if xport_type == "udp": + return self._xport_mgrs[xport_type].get_chdr_link_options( + self.mboard_info['rpc_connection']) + # else: + return self._xport_mgrs[xport_type].get_chdr_link_options() + + ########################################################################### + # Device info + ########################################################################### + def get_device_info_dyn(self): + """ + Append the device info with current IP addresses. + """ + if not self._device_initialized: + return {} + device_info = self._xport_mgrs['udp'].get_xport_info() + device_info.update({ + 'fpga_version': "{}.{}".format( + *self.mboard_regs_control.get_compat_number()), + 'fpga_version_hash': "{:x}.{}".format( + *self.mboard_regs_control.get_git_hash()), + 'fpga': self.updateable_components.get('fpga', {}).get('type', ""), + }) + return device_info + + def is_db_gpio_ifc_present(self, slot_id): + """ + Return if daughterboard GPIO interface at 'slot_id' is present in the FPGA + """ + db_gpio_version = self.mboard_regs_control.get_db_gpio_ifc_version(slot_id) + return db_gpio_version[0] > 0 + + ########################################################################### + # Clock/Time API + ########################################################################### + def get_clock_sources(self): + """ + Lists all available clock sources. + """ + return self._clk_mgr.get_clock_sources() + + def set_clock_source(self, *args): + """ + Ensures the new reference clock source and current time source pairing + is valid and sets both by calling set_sync_source(). + """ + clock_source = args[0] + time_source = self._clk_mgr.get_time_source() + assert clock_source is not None + assert time_source is not None + if (clock_source, time_source) not in self._clk_mgr.valid_sync_sources: + old_time_source = time_source + if clock_source in ( + X4xxClockMgr.CLOCK_SOURCE_MBOARD, + X4xxClockMgr.CLOCK_SOURCE_INTERNAL): + time_source = X4xxClockMgr.TIME_SOURCE_INTERNAL + elif clock_source == X4xxClockMgr.CLOCK_SOURCE_EXTERNAL: + time_source = X4xxClockMgr.TIME_SOURCE_EXTERNAL + elif clock_source == X4xxClockMgr.CLOCK_SOURCE_GPSDO: + time_source = X4xxClockMgr.TIME_SOURCE_GPSDO + self.log.warning( + f"Time source '{old_time_source}' is an invalid selection with " + f"clock source '{clock_source}'. " + f"Coercing time source to '{time_source}'") + self.set_sync_source({ + "clock_source": clock_source, "time_source": time_source}) + + def set_clock_source_out(self, enable): + """ + Allows routing the clock configured as source on the clk aux board to + the RefOut terminal. This only applies to internal, gpsdo and nsync. + """ + self._clk_mgr.set_clock_source_out(enable) + + def get_time_sources(self): + " Returns list of valid time sources " + return self._clk_mgr.get_time_sources() + + def set_time_source(self, time_source): + """ + Set a time source + + This will call set_sync_source() internally, and use the current clock + source as a clock source. If the current clock source plus the requested + time source is not a valid combination, it will coerce the clock source + to a valid choice and print a warning. + """ + clock_source = self._clk_mgr.get_clock_source() + assert clock_source is not None + assert time_source is not None + if (clock_source, time_source) not in self._clk_mgr.valid_sync_sources: + old_clock_source = clock_source + if time_source == X4xxClockMgr.TIME_SOURCE_QSFP0: + clock_source = X4xxClockMgr.CLOCK_SOURCE_MBOARD + elif time_source == X4xxClockMgr.TIME_SOURCE_INTERNAL: + clock_source = X4xxClockMgr.CLOCK_SOURCE_MBOARD + elif time_source == X4xxClockMgr.TIME_SOURCE_EXTERNAL: + clock_source = X4xxClockMgr.CLOCK_SOURCE_EXTERNAL + elif time_source == X4xxClockMgr.TIME_SOURCE_GPSDO: + clock_source = X4xxClockMgr.CLOCK_SOURCE_GPSDO + self.log.warning( + 'Clock source {} is an invalid selection with time source {}. ' + 'Coercing clock source to {}' + .format(old_clock_source, time_source, clock_source)) + self.set_sync_source( + {"time_source": time_source, "clock_source": clock_source}) + + def set_sync_source(self, args): + """ + Selects reference clock and PPS sources. Unconditionally re-applies the + time source to ensure continuity between the reference clock and time + rates. + Note that if we change the source such that the time source is changed + to 'external', then we need to also disable exporting the reference + clock (RefOut and PPS-In are the same SMA connector). + """ + # Check the clock source, time source, and combined pair are valid: + clock_source = args.get('clock_source', self._clk_mgr.get_clock_source()) + if clock_source not in self.get_clock_sources(): + raise ValueError(f'Clock source {clock_source} is not a valid selection') + time_source = args.get('time_source', self._clk_mgr.get_time_source()) + if time_source not in self.get_time_sources(): + raise ValueError(f'Time source {time_source} is not a valid selection') + if (clock_source, time_source) not in self._clk_mgr.valid_sync_sources: + raise ValueError( + f'Clock and time source pair ({clock_source}, {time_source}) is ' + 'not a valid selection') + # Sanity checks complete. Now check if we need to disable the RefOut. + # Reminder: RefOut and PPSIn share an SMA. Besides, you can't export an + # external clock. We are thus not checking for time_source == 'external' + # because that's a subset of clock_source == 'external'. + # We also disable clock exports for 'mboard', because the mboard clock + # does not get routed back to the clocking aux board and thus can't be + # exported either. + if clock_source in (X4xxClockMgr.CLOCK_SOURCE_EXTERNAL, + X4xxClockMgr.CLOCK_SOURCE_MBOARD) and \ + self._clocking_auxbrd: + self._clocking_auxbrd.export_clock(enable=False) + # Now the clock manager can do its thing. + ret_val = self._clk_mgr.set_sync_source(clock_source, time_source) + if ret_val == self._clk_mgr.SetSyncRetVal.NOP: + return + try: + # Re-set master clock rate. If this doesn't work, it will time out + # and throw an exception. We need to put the device back into a safe + # state in that case. + self.set_master_clock_rate(self._master_clock_rate) + except RuntimeError as ex: + err = f"Setting clock_source={clock_source},time_source={time_source} " \ + f"failed, falling back to {self._safe_sync_source}. Error: " \ + f"{ex}" + self.log.error(err) + if args.get('__noretry__', False): + self.log.error("Giving up.") + else: + self.set_sync_source({**self._safe_sync_source, '__noretry__': True}) + raise + + def set_master_clock_rate(self, master_clock_rate): + """ + Sets the master clock rate by configuring the RFDC decimation and SPLL, + and then resetting downstream clocks. + """ + if master_clock_rate not in self.rfdc.master_to_sample_clk: + self.log.error('Unsupported master clock rate selection {}' + .format(master_clock_rate)) + raise RuntimeError('Unsupported master clock rate selection') + sample_clock_freq, decimation, is_legacy_mode, halfband = \ + self.rfdc.master_to_sample_clk[master_clock_rate] + for db_idx, _ in enumerate(self.dboards): + db_rfdc_resamp, db_halfband = self.rfdc.get_rfdc_resampling_factor(db_idx) + if db_rfdc_resamp != decimation or db_halfband != halfband: + msg = (f'master_clock_rate {master_clock_rate} is not compatible ' + f'with FPGA which expected decimation {db_rfdc_resamp}') + self.log.error(msg) + raise RuntimeError(msg) + self.log.trace(f"Set master clock rate (SPLL) to: {master_clock_rate}") + self._clk_mgr.set_spll_rate(sample_clock_freq, is_legacy_mode) + self._master_clock_rate = master_clock_rate + self.rfdc.sync() + self._clk_mgr.config_pps_to_timekeeper(master_clock_rate) + + def set_trigger_io(self, direction): + """ + Switch direction of clocking board Trigger I/O SMA socket. + IMPORTANT! Ensure downstream devices depending on TRIG I/O's output ignore + this signal when calling this method or re-run their synchronization routine + after calling this method. The output-enable control is async. to the output. + :param self: + :param direction: "off" trigger io socket unused + "pps_output" device will output PPS signal + "input" PPS is fed into the device from external + :return: success status as boolean + """ + OFF = "off" + INPUT = "input" + PPS_OUTPUT = "pps_output" + directions = [OFF, INPUT, PPS_OUTPUT] + + if not self._clocking_auxbrd: + raise RuntimeError("No clocking aux board available") + if not direction in directions: + raise RuntimeError("Invalid trigger io direction (%s). Use one of %s" + % (direction, directions)) + + # Switching order of trigger I/O lines depends on requested direction. + # Always turn on new driver last so both drivers cannot be active + # simultaneously. + if direction == INPUT: + self.mboard_regs_control.set_trig_io_output(False) + self._clocking_auxbrd.set_trig(1, ClockingAuxBrdControl.DIRECTION_INPUT) + elif direction == PPS_OUTPUT: + self._clocking_auxbrd.set_trig(1, ClockingAuxBrdControl.DIRECTION_OUTPUT) + self.mboard_regs_control.set_trig_io_output(True) + else: # direction == OFF: + self.mboard_regs_control.set_trig_io_output(False) + self._clocking_auxbrd.set_trig(0) + + return True + + ########################################################################### + # EEPROMs + ########################################################################### + def get_db_eeprom(self, dboard_idx): + """ + See PeriphManagerBase.get_db_eeprom() for docs. + """ + try: + dboard = self.dboards[dboard_idx] + except IndexError: + error_msg = "Attempted to access invalid dboard index `{}' " \ + "in get_db_eeprom()!".format(dboard_idx) + self.log.error(error_msg) + raise RuntimeError(error_msg) + db_eeprom_data = copy.copy(dboard.device_info) + return db_eeprom_data + + ########################################################################### + # Component updating + ########################################################################### + # Note: Component updating functions defined by ZynqComponents + @no_rpc + def _update_fpga_type(self): + """Update the fpga type stored in the updateable components""" + fpga_string = "{}_{}".format( + self.mboard_regs_control.get_fpga_type(), + self.rfdc.get_dsp_bw()) + self.log.debug("Updating mboard FPGA type info to {}".format(fpga_string)) + self.updateable_components['fpga']['type'] = fpga_string + + ####################################################################### + # Timekeeper API + ####################################################################### + def get_master_clock_rate(self): + """ Return the master clock rate set during init """ + return self._master_clock_rate + + def get_clocks(self): + """ + Gets the RFNoC-related clocks present in the FPGA design + """ + # TODO: The 200 and 40 MHz clocks should not be hard coded, and ideally + # be linked to the FPGA image somehow + return [ + { + 'name': 'radio_clk', + 'freq': str(self.get_master_clock_rate()), + 'mutable': 'true' + }, + { + 'name': 'bus_clk', + 'freq': str(200e6), + }, + { + 'name': 'ctrl_clk', + 'freq': str(40e6), + } + ] + + + ########################################################################### + # Utility for validating RPU core number + ########################################################################### + @no_rpc + def _validate_rpu_core_number(self, core_number): + if ((core_number < 0) or (core_number > 1)): + raise RuntimeError("RPU core number must be 0 or 1.") + + + ########################################################################### + # Utility for validating RPU state change command + ########################################################################### + @no_rpc + def _validate_rpu_state(self, new_state_command, previous_state): + if ((new_state_command != RPU_STATE_COMMAND_START) + and (new_state_command != RPU_STATE_COMMAND_STOP)): + raise RuntimeError("RPU state command must be start or stop.") + if ((new_state_command == RPU_STATE_COMMAND_START) + and (previous_state == RPU_STATE_RUNNING)): + raise RuntimeError("RPU already running.") + if ((new_state_command == RPU_STATE_COMMAND_STOP) + and (previous_state == RPU_STATE_OFFLINE)): + raise RuntimeError("RPU already offline.") + + ########################################################################### + # Utility for validating RPU firmware + ########################################################################### + @no_rpc + def _validate_rpu_firmware(self, firmware): + file_path = path.join(RPU_REMOTEPROC_FIRMWARE_PATH, firmware) + if not path.isfile(file_path): + raise RuntimeError("Specified firmware does not exist.") + + ########################################################################### + # Utility for reading contents of a file + ########################################################################### + @no_rpc + def _read_file(self, file_path): + self.log.trace("_read_file: file_path= %s", file_path) + with open(file_path, 'r') as f: + return f.read().strip() + + + ########################################################################### + # Utility for writing contents of a file + ########################################################################### + @no_rpc + def _write_file(self, file_path, data): + self.log.trace("_write_file: file_path= %s, data= %s", file_path, data) + with open(file_path, 'w') as f: + f.write(data) + + + ########################################################################### + # RPU Image Deployment API + ########################################################################### + def get_rpu_state(self, core_number, validate=True): + """ Report the state for the specified RPU core """ + if validate: + self._validate_rpu_core_number(core_number) + return self._read_file( + path.join( + RPU_REMOTEPROC_PREFIX_PATH + str(core_number), + RPU_REMOTEPROC_PROPERTY_STATE)) + + + def set_rpu_state(self, core_number, new_state_command, validate=True): + """ Set the specified state for the specified RPU core """ + if not self._rpu_initialized: + self.log.warning( + "Failed to set RPU state: RPU peripheral not "\ + "initialized.") + return RPU_FAILURE_REPORT + # OK, RPU is initialized, now go set its state: + if validate: + self._validate_rpu_core_number(core_number) + previous_state = self.get_rpu_state(core_number, False) + if validate: + self._validate_rpu_state(new_state_command, previous_state) + self._write_file( + path.join( + RPU_REMOTEPROC_PREFIX_PATH + str(core_number), + RPU_REMOTEPROC_PROPERTY_STATE), + new_state_command) + + # Give RPU core time to change state (might load new fw) + poll_with_timeout( + lambda: previous_state != self.get_rpu_state(core_number, False), + RPU_MAX_STATE_CHANGE_TIME_IN_MS, + RPU_STATE_CHANGE_POLLING_INTERVAL_IN_MS) + + # Quick validation of new state + resulting_state = self.get_rpu_state(core_number, False) + if ((new_state_command == RPU_STATE_COMMAND_START) + and (resulting_state != RPU_STATE_RUNNING)): + raise RuntimeError('Unable to start specified RPU core.') + if ((new_state_command == RPU_STATE_COMMAND_STOP) + and (resulting_state != RPU_STATE_OFFLINE)): + raise RuntimeError('Unable to stop specified RPU core.') + return RPU_SUCCESS_REPORT + + def get_rpu_firmware(self, core_number): + """ Report the firmware for the specified RPU core """ + self._validate_rpu_core_number(core_number) + return self._read_file( + path.join( + RPU_REMOTEPROC_PREFIX_PATH + str(core_number), + RPU_REMOTEPROC_PROPERTY_FIRMWARE)) + + + def set_rpu_firmware(self, core_number, firmware, start=0): + """ Deploy the image at the specified path to the RPU """ + self.log.trace("set_rpu_firmware") + self.log.trace( + "image path: %s, core_number: %d, start?: %d", + firmware, + core_number, + start) + + if not self._rpu_initialized: + self.log.warning( + "Failed to deploy RPU image: "\ + "RPU peripheral not initialized.") + return RPU_FAILURE_REPORT + # RPU is initialized, now go set firmware: + self._validate_rpu_core_number(core_number) + self._validate_rpu_firmware(firmware) + + # Stop the core if necessary + if self.get_rpu_state(core_number, False) == RPU_STATE_RUNNING: + self.set_rpu_state(core_number, RPU_STATE_COMMAND_STOP, False) + + # Set the new firmware path + self._write_file( + path.join( + RPU_REMOTEPROC_PREFIX_PATH + str(core_number), + RPU_REMOTEPROC_PROPERTY_FIRMWARE), + firmware) + + # Start the core if requested + if start != 0: + self.set_rpu_state(core_number, RPU_STATE_COMMAND_START, False) + return RPU_SUCCESS_REPORT + + ####################################################################### + # Debugging + # Provides temporary methods for arbitrary hardware access while + # development for these components is ongoing. + ####################################################################### + def peek_ctrlport(self, addr): + """ Peek the MPM Endpoint to ctrlport registers on the FPGA """ + return '0x{:X}'.format(self.ctrlport_regs.peek32(addr)) + + def poke_ctrlport(self, addr, val): + """ Poke the MPM Endpoint to ctrlport registers on the FPGA """ + self.ctrlport_regs.poke32(addr, val) + + def peek_cpld(self, addr): + """ Peek the PS portion of the MB CPLD """ + return '0x{:X}'.format(self.cpld_control.peek32(addr)) + + def poke_cpld(self, addr, val): + """ Poke the PS portion of the MB CPLD """ + self.cpld_control.poke32(addr, val) + + def peek_db(self, db_id, addr): + """ Peek the DB CPLD, even if the DB is not discovered by MPM """ + assert db_id in (0, 1) + self.cpld_control.enable_daughterboard(db_id) + return '0x{:X}'.format( + self.ctrlport_regs.get_db_cpld_iface(db_id).peek32(addr)) + + def poke_db(self, db_id, addr, val): + """ Poke the DB CPLD, even if the DB is not discovered by MPM """ + assert db_id in (0, 1) + self.cpld_control.enable_daughterboard(db_id) + self.ctrlport_regs.get_db_cpld_iface(db_id).poke32(addr, val) + + def peek_clkaux(self, addr): + """Peek the ClkAux DB over SPI""" + return '0x{:X}'.format(self._clocking_auxbrd.peek8(addr)) + + def poke_clkaux(self, addr, val): + """Poke the ClkAux DB over SPI""" + self._clocking_auxbrd.poke8(addr, val) + + ########################################################################### + # Sensors + ########################################################################### + def get_ref_lock_sensor(self): + """ + Return main refclock lock status. This is the lock status of the + reference and sample PLLs. + """ + lock_status = self._clk_mgr.get_ref_locked() + return { + 'name': 'ref_locked', + 'type': 'BOOLEAN', + 'unit': 'locked' if lock_status else 'unlocked', + 'value': str(lock_status).lower(), + } + + def get_fpga_temp_sensor(self): + """ Get temperature sensor reading of the X4xx FPGA. """ + self.log.trace("Reading FPGA temperature.") + return get_temp_sensor(["RFSoC"], log=self.log) + + def get_main_power_temp_sensor(self): + """ + Get temperature sensor reading of PM-BUS devices which supply + 0.85V power supply to RFSoC. + """ + self.log.trace("Reading PMBus Power Supply Chip(s) temperature.") + return get_temp_sensor(["PMBUS-0", "PMBUS-1"], log=self.log) + + def get_scu_internal_temp_sensor(self): + """ Get temperature sensor reading of STM32 SCU's internal sensor. """ + self.log.trace("Reading SCU internal temperature.") + return get_temp_sensor(["EC Internal"], log=self.log) + + def get_internal_temp_sensor(self): + """ TODO: Determine how to interpret this function """ + self.log.warning("Reading internal temperature is not yet implemented.") + return { + 'name': 'temperature', + 'type': 'REALNUM', + 'unit': 'C', + 'value': '-1' + } + + def _get_fan_sensor(self, fan='fan0'): + """ Get fan speed. """ + self.log.trace("Reading {} speed sensor.".format(fan)) + fan_rpm = -1 + try: + fan_rpm_all = ectool.get_fan_rpm() + fan_rpm = fan_rpm_all[fan] + except Exception as ex: + self.log.warning( + "Error occurred when getting {} speed value: {} " + .format(fan, str(ex))) + return { + 'name': fan, + 'type': 'INTEGER', + 'unit': 'rpm', + 'value': str(fan_rpm) + } + + def get_fan0_sensor(self): + """ Get fan0 speed. """ + return self._get_fan_sensor('fan0') + + def get_fan1_sensor(self): + """ Get fan1 speed.""" + return self._get_fan_sensor('fan1') + + def get_gps_sensor_status(self): + """ + Get all status of GPS as sensor dict + """ + assert self._gps_mgr + self.log.trace("Reading all GPS status pins") + return f""" + {self.get_gps_lock_sensor()} + {self.get_gps_alarm_sensor()} + {self.get_gps_warmup_sensor()} + {self.get_gps_survey_sensor()} + {self.get_gps_phase_lock_sensor()} + """ diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_clk_aux.py b/mpm/python/usrp_mpm/periph_manager/x4xx_clk_aux.py new file mode 100644 index 000000000..fcd655d9d --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_clk_aux.py @@ -0,0 +1,682 @@ +# +# Copyright 2021 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Drivers for the X410 clocking aux board. +""" + +import subprocess +from usrp_mpm import lib # Pulls in everything from C++-land +from usrp_mpm import tlv_eeprom +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.sys_utils.gpio import Gpio +from usrp_mpm.sys_utils.udev import dt_symbol_get_spidev +from usrp_mpm.sys_utils.udev import get_eeprom_paths_by_symbol +from usrp_mpm.sys_utils import i2c_dev +from usrp_mpm.chips import LMK05318 + +X400_CLKAUX_DEFAULT_TUNING_WORD = 0x200 # 1.65V which would be a DAC value of 512 +X400_CLKAUX_DEFAULT_REVISION = 0x1 +X400_CLKAUX_I2C_LABEL = 'clkaux_i2c' +X400_CLKAUX_SPI_LABEL = 'clkaux_spi' +X400_CLKAUX_GPSDO_PID = 0x4004 +X400_CLKAUX_NOGPSDO_PID = 0x4005 + + +def _check_i2c_bus(): + """ + Assert that the I2C connection to the clocking board is available in the + device tree. + """ + i2c_bus = i2c_dev.dt_symbol_get_i2c_bus(X400_CLKAUX_I2C_LABEL) + if i2c_bus is None: + raise RuntimeError("ClockingAuxBrdControl I2C bus not found") + +def _check_spi_bus(): + """ + Assert that the SPI connection to the clocking board is available in the + device tree. + """ + spi_node = dt_symbol_get_spidev(X400_CLKAUX_SPI_LABEL) + if spi_node is None: + raise RuntimeError("ClockingAuxBrdControl SPI bus not found") + + +# pylint: disable=too-few-public-methods +class ClkAuxTagMap: + """ + x4xx main tagmap is in the main class. Only the subset relevant to the + clocking aux board is included below. + """ + magic = 0x55535250 + tagmap = { + # 0x23: usrp_eeprom_clkaux_tuning_word + 0x23: tlv_eeprom.NamedStruct('< H', + ['tuning_word']), + # 0x10: usrp_eeprom_board_info + 0x10: tlv_eeprom.NamedStruct('< H H H 7s 1x', + ['pid', 'rev', 'rev_compat', 'serial']), + } + +# We are encapsulating a complicated piece of hardware here, so let's live with +# the fact that there will be many things hanging off of it. +# pylint: disable=too-many-public-methods +# pylint: disable=too-many-instance-attributes +class ClockingAuxBrdControl: + """ + Control interface for the Clocking Aux Board over I2C and SPI + """ + SOURCE_INTERNAL = "internal" + SOURCE_EXTERNAL = "external" + SOURCE_GPSDO = "gpsdo" + SOURCE_NSYNC = "nsync" + + SOURCE_NSYNC_LMK_PRI_FABRIC_CLK = "fabric_clk" + SOURCE_NSYNC_LMK_PRI_GTY_RCV_CLK = "gty_rcv_clk" + + NSYNC_PRI_REF = "pri_ref" + NSYNC_SEC_REF = "sec_ref" + + DIRECTION_INPUT = "input" + DIRECTION_OUTPUT = "output" + + VALID_CLK_EXPORTS = (SOURCE_INTERNAL, SOURCE_GPSDO, SOURCE_NSYNC) + VALID_NSYNC_LMK_PRI_REF_SOURCES = (SOURCE_NSYNC_LMK_PRI_FABRIC_CLK, + SOURCE_NSYNC_LMK_PRI_GTY_RCV_CLK) + + def __init__(self, default_source=None, parent_log=None): + self.log = \ + parent_log.getChild(self.__class__.__name__) if parent_log is not None \ + else get_logger(self.__class__.__name__) + _check_i2c_bus() + self._revision = self._get_eeprom_field('rev', X400_CLKAUX_DEFAULT_REVISION) + self._pid = self._get_eeprom_field('pid', X400_CLKAUX_NOGPSDO_PID) + self._nsync_support = self._revision >= 2 + self._gps_support = self._pid == X400_CLKAUX_GPSDO_PID + default_source = default_source or ClockingAuxBrdControl.SOURCE_INTERNAL + + # Some GPIO lines are named differently in the overlays for rev 1 and + # rev 2 and some perform different functions in rev 1 and 2 even if + # named similarly. + + # GPIO common to rev 1 and 2 + self._3v3_power_good = Gpio("CLKAUX_3V3_CLK_PG", Gpio.INPUT) + self._pps_term = Gpio("CLKAUX_PPS_TERM", Gpio.OUTPUT, 1) + self._trig_oe_n = Gpio("CLKAUX_TRIG_OEn", Gpio.OUTPUT, 1) + self._trig_dir = Gpio("CLKAUX_TRIG_DIR", Gpio.OUTPUT, 0) + self._ref_lck_led = Gpio("CLKAUX_REF_LCK", Gpio.OUTPUT, 0) + + if self._revision == 1: + self._ref_clk_sel_usr = Gpio("CLKAUX_REF_CLK_SEL_USR", Gpio.OUTPUT, 1) + self._mbrefclk_bias = Gpio("CLKAUX_MBRefCLK_En", Gpio.OUTPUT, 0) + self._exportclk_bias = Gpio("CLKAUX_ExportClk_En", Gpio.OUTPUT, 0) + elif self._revision >= 2: + self._ref_clk_sel_usr = Gpio("CLKAUX_UsrRefSel", Gpio.OUTPUT, 0) + self._mbrefclk_bias = Gpio("CLKAUX_MBRefCLK_Bias", Gpio.OUTPUT, 0) + self._exportclk_bias = Gpio("CLKAUX_ExportClk_Bias", Gpio.OUTPUT, 0) + self._tcxo_en = Gpio("CLKAUX_TCXO_EN", Gpio.OUTPUT, 0) + self._exportclk_en = Gpio("CLKAUX_EXP_CLK_EN", Gpio.OUTPUT, 0) + self._nsync_refsel = Gpio("CLKAUX_NSYNC_REFSEL", Gpio.OUTPUT, 0) + self._nsync_power_ctrl = Gpio("CLKAUX_NSYNC_PDN", Gpio.OUTPUT, 0) + self._ref_clk_select_net = Gpio("CLKAUX_REF_CLK_SEL_NET", Gpio.OUTPUT, 0) + self._nsync_gpio0 = Gpio("CLKAUX_NSYNC_GPIO0", Gpio.INPUT) + self._nsync_status1 = Gpio("CLKAUX_NSYNC_STATUS1", Gpio.INPUT) + self._nsync_status0 = Gpio("CLKAUX_NSYNC_STATUS0", Gpio.INPUT) + self._fpga_clk_gty_fabric_sel = Gpio("CLKAUX_FPGA_CLK_SEL", Gpio.OUTPUT, 0) + self._lmk05318_bias = Gpio("CLKAUX_05318Ref_Bias", Gpio.OUTPUT, 0) + + if self._gps_support: + self._gps_phase_lock = Gpio("CLKAUX_GPS_PHASELOCK", Gpio.INPUT) + self._gps_warmup = Gpio("CLKAUX_GPS_WARMUP", Gpio.INPUT) + self._gps_survey = Gpio("CLKAUX_GPS_SURVEY", Gpio.INPUT) + self._gps_lock = Gpio("CLKAUX_GPS_LOCK", Gpio.INPUT) + self._gps_alarm = Gpio("CLKAUX_GPS_ALARM", Gpio.INPUT) + self._gps_rst_n = Gpio("CLKAUX_GPS_RSTn", Gpio.OUTPUT, 0) + if self._revision == 1: + self._gps_initsrv_n = Gpio("CLKAUX_GPS_INITSRVn", Gpio.OUTPUT, 1) + elif self._revision >= 2: + self._gps_initsrv_n = Gpio("CLKAUX_GPS_INITSURVn", Gpio.OUTPUT, 0) + + if self._revision >= 2: + _check_spi_bus() + # Create SPI interface to the LMK05318 registers + nsync_spi_node = dt_symbol_get_spidev(X400_CLKAUX_SPI_LABEL) + nsync_lmk_regs_iface = lib.spi.make_spidev_regs_iface( + nsync_spi_node, + 1000000, # Speed (Hz) + 0x0, # SPI mode + 8, # Addr shift + 0, # Data shift + 1<<23, # Read flag + 0, # Write flag + ) + self._nsync_pll = LMK05318(nsync_lmk_regs_iface, self.log) + + self.set_source(default_source) + self.set_trig(False, self.DIRECTION_OUTPUT) + self._init_dac() + + def _init_dac(self): + """ + Initializes i2c bus to communicate with the DAC and configures the + tuning word for both voltage outputs + """ + dac_i2c_bus = i2c_dev.dt_symbol_get_i2c_bus(X400_CLKAUX_I2C_LABEL) + self._dac_i2c_iface = lib.i2c.make_i2cdev( + dac_i2c_bus, + 0xC, # addr + False, # ten_bit_addr + 100 + ) + tuning_word = self._get_eeprom_field('tuning_word', X400_CLKAUX_DEFAULT_TUNING_WORD) + self.config_dac(tuning_word, 0) + self.config_dac(tuning_word, 1) + + def _get_eeprom_field(self, field_name, default_val): + """ + Return the value of the requested eeprom field. + """ + eeprom_paths = get_eeprom_paths_by_symbol("clkaux_eeprom") + path = eeprom_paths['clkaux_eeprom'] + val = default_val + try: + eeprom, _ = tlv_eeprom.read_eeprom( + path, ClkAuxTagMap.tagmap, ClkAuxTagMap.magic, None) + val = eeprom.get(field_name, default_val) + except TypeError as err: + self.log.warning(f"Error reading eeprom; will use defaults. ({err})") + return val + + def store_tuning_word(self, tuning_word): + """ Store the dac tuning word in the ID EEPROM""" + cmd = ["eeprom-update", + "clkaux", + "--clkaux_tuning_word", + str(tuning_word)] + try: + subprocess.call(cmd) + except subprocess.CalledProcessError as ex: + self.log.warning("Failed to write to clkaux EEPROM: %s", str(ex)) + + def config_dac(self, tuning_word, out_select): + """Configure tuning word on the the selected DAC output through i2c""" + high, low = divmod(tuning_word << 6, 0x100) + command = 0x38 if out_select else 0x31 + tx_cmd = [command, high, low] + # Send command to write tuning word and update output + self._dac_i2c_iface.transfer(tx_cmd, 0, True) + + def read_dac(self, out_select): + """Read the tuning word on the selected DAC output through i2c""" + command = 0x8 if out_select else 0x1 + rx_buffer = self._dac_i2c_iface.transfer([command], 2, True) + val = ((rx_buffer[0] << 8) | rx_buffer[1]) >> 6 + return val + + def is_nsync_supported(self): + """Return True if nsync clock source is supported by hardware""" + return self._nsync_support + + def _check_nsync_supported(self): + """ + Assert nsync clock source is supported by hardware or throw otherwise + """ + if not self.is_nsync_supported(): + raise RuntimeError("NSYNC related features are not supported!") + + def is_gps_supported(self): + """Return True if GPS clock source is supported by hardware""" + return self._gps_support + + def is_gps_enabled(self): + """ + Return True if the GPS is currently enabled (i.e., not held in reset). + """ + return bool(self._gps_rst_n.get()) + + def _assert_gps_supported(self): + """ Throw a RuntimeError if GPS is not supported on this board. """ + if not self.is_gps_supported(): + raise RuntimeError("GPS related features are not supported!") + + def _init_nsync_lmk(self): + """Initialize the LMK05318 network sync IC""" + self._check_nsync_supported() + self.set_nsync_lmk_power_en(1) + self._nsync_pll.soft_reset(True) + self._nsync_pll.soft_reset(False) + if not self._nsync_pll.is_chip_id_valid(): + raise RuntimeError("ClockingAuxBrdControl Unable to locate LMK05318!") + + def set_source(self, clock_source, time_source=None): + """ + Select the clock and time source on the clock auxbrd. + + Notes: + - The clocking aux board has a PPS termination which must be disabled + when the PPS input is active. Note that we can only have an external + time source when the clock source is also external. We enable it in + all other cases. + - The pin that selects the reference clock (UsrRefSel or ref_clk_sel_usr) + selects *both* the clock reference and the time reference (external + or GPSDO). So if clock source is set to external, but time source to + internal, then we are still connecting the PPS In SMA to the FPGA. + - This function will disable clock export if we switch to external clock. + - The time source is irrelevant unless the clock source is EXTERNAL, so + we allow not specifying it for the other cases. + - We actually put the GPS chip into reset when it's unused so we don't + collect spurs from there. However, this means the GPS will need to + warm up when being selected. Selecting the GPS as a source at runtime + is possible, it just won't be available until it's warmed up. + """ + if clock_source not in self.VALID_CLK_EXPORTS: + self.export_clock(False) + if clock_source == self.SOURCE_INTERNAL: + self._set_gps_rstn(0) + self._set_ref_clk_sel_usr(0) + # If we are using an internal PPS, then we terminate the connector + use_pps_term = time_source == self.SOURCE_INTERNAL + self._pps_term.set(use_pps_term) + self._mbrefclk_bias.set(1) + if self._revision >= 2: + self.set_nsync_lmk_power_en(0) + self._lmk05318_bias.set(0) + self._ref_clk_select_net.set(0) + elif clock_source == self.SOURCE_EXTERNAL: + self._set_gps_rstn(0) + self._set_ref_clk_sel_usr(1) + self._exportclk_bias.set(0) + self._pps_term.set(1) + self._mbrefclk_bias.set(1) + if self._revision >= 2: + self.set_nsync_lmk_power_en(0) + self._lmk05318_bias.set(0) + self._ref_clk_select_net.set(0) + elif clock_source == self.SOURCE_GPSDO: + self._assert_gps_supported() + self._set_gps_rstn(1) + self._set_ref_clk_sel_usr(0) + self._pps_term.set(1) + self._mbrefclk_bias.set(1) + if self._revision >= 2: + self.set_nsync_lmk_power_en(0) + self._lmk05318_bias.set(0) + self._ref_clk_select_net.set(0) + elif clock_source == self.SOURCE_NSYNC: + self._check_nsync_supported() + self._set_gps_rstn(0) + self._set_ref_clk_sel_usr(0) + self._tcxo_en.set(1) + self._nsync_refsel.set(1) + self._mbrefclk_bias.set(0) + self._lmk05318_bias.set(1) + self._pps_term.set(1) + self._ref_clk_select_net.set(1) + self._init_nsync_lmk() + else: + raise RuntimeError('Invalid clock source {}'.format(clock_source)) + self._source = clock_source + self.log.trace("set clock source to: {}".format(self._source)) + + + def export_clock(self, enable=True): + """Export clock source to RefOut""" + clock_source = self.get_clock_source() + if not enable: + self._exportclk_bias.set(0) + self._pps_term.set(1) + if self._revision >= 2: + self._exportclk_en.set(0) + elif clock_source in self.VALID_CLK_EXPORTS: + self._exportclk_bias.set(1) + self._pps_term.set(0) + if self._revision >= 2: + self._exportclk_en.set(1) + else: + raise RuntimeError('Invalid source to export: {}'.format(clock_source)) + + def set_trig(self, enable, direction=None): + """Enable/disable the Trig IO out""" + if direction is None: + direction = ClockingAuxBrdControl.DIRECTION_OUTPUT + + if enable: + self._trig_oe_n.set(0) + else: + self._trig_oe_n.set(1) + + if direction == self.DIRECTION_INPUT: + self._trig_dir.set(0) + elif direction == self.DIRECTION_OUTPUT: + self._trig_dir.set(1) + else: + raise RuntimeError( + 'Invalid direction {}, valid options are {} and {}' + .format(direction, self.DIRECTION_INPUT, self.DIRECTION_OUTPUT)) + + def get_clock_source(self): + """Returns the clock source""" + return self._source + + def get_gps_phase_lock(self): + """Returns true if the GPS Phase is locked, and false if it is not""" + return self._gps_phase_lock.get() + + def get_gps_warmup(self): + """Returns true if the GPS is warming up""" + return self._gps_warmup.get() + + def get_gps_survey(self): + """Returns whether or not an auto survey is in progress""" + return self._gps_survey.get() + + def get_gps_lock(self): + """Returns whether or not the GPS has a lock""" + return self._gps_lock.get() + + def get_gps_alarm(self): + """Returns true if the GPS detects a hardware fault or software alarm""" + return self._gps_alarm.get() + + def get_3v3_pg(self): + """Returns true if the 3.3V rail is good, false otherwise""" + return self._3v3_power_good.get() + + def _set_ref_clk_sel_usr(self, value): + """Sets REF_CLK_SEL_USR to value""" + value = int(value) + assert value in (0, 1) + if value == 1: + #Never set REF_CLK_SEL_USR and GPS_RSTn high at the same time, hardware can be damaged + self._set_gps_rstn(0) + self._ref_clk_sel_usr.set(value) + + def _set_gps_rstn(self, value): + """ + Sets GPS_RSTn to value + + If value == 0, then the GPS is held in reset and is not usable. + """ + value = int(value) + assert value in (0, 1) + if value == 1: + # Never set REF_CLK_SEL_USR and GPS_RSTn high at the same time, + # hardware can be damaged + self._set_ref_clk_sel_usr(0) + if self._gps_support: + self._gps_rst_n.set(value) + elif value == 1: + raise RuntimeError("No GPS, so can't bring it out of reset") + + def get_nsync_chip_id_valid(self): + """Returns whether the chip ID of the LMK is valid""" + return self._nsync_pll.is_chip_id_valid() + + def set_nsync_soft_reset(self, value=True): + """Soft reset LMK chip""" + return self._nsync_pll.soft_reset(value) + + def get_nsync_status0(self): + """Returns value of STATUS0 pin on LMK05318 NSYNC IC""" + self._check_nsync_supported() + return self._nsync_status0.get() + + def get_nsync_status1(self): + """Returns value of STATUS1 pin on LMK05318 NSYNC IC""" + self._check_nsync_supported() + return self._nsync_status1.get() + + def set_nsync_pri_ref_source(self, source): + """Sets LMK05318 PRIMREF (primary reference) to specified source""" + self._check_nsync_supported() + + if source not in self.VALID_NSYNC_LMK_PRI_REF_SOURCES: + raise RuntimeError( + "Invalid primary reference clock source for LMK05318 NSYNC IC") + + self.config_dpll(source) + if source == self.SOURCE_NSYNC_LMK_PRI_FABRIC_CLK: + self._fpga_clk_gty_fabric_sel.set(1) + else: + self._fpga_clk_gty_fabric_sel.set(0) + + def set_nsync_ref_select(self, source): + """Sets LMK05318 REFSEL to PRIREF or SECREF""" + self._check_nsync_supported() + if source == self.NSYNC_PRI_REF: + self._nsync_refsel.set(0) + elif source == self.NSYNC_SEC_REF: + self._nsync_refsel.set(1) + else: + raise RuntimeError( + "Invalid setting for LMK05318 NSYNC REFSEL") + + def set_nsync_tcxo_en(self, enable): + """ + Enables/Disables the 10 MHz TCXO chip output; this signal serves as the + oscillator input to the LMK05318 NSYNC IC. + """ + self._check_nsync_supported() + if enable: + self._tcxo_en.set(1) + else: + self._tcxo_en.set(0) + + def set_nsync_lmk_power_en(self, enable): + """Turn on/off the LMK05318 IC using the PDN pin""" + self._check_nsync_supported() + if enable: + self.log.trace("enable LMK05318 power") + self._nsync_power_ctrl.set(1) + else: + self.log.trace("disable LMK05318 power") + self._nsync_power_ctrl.set(0) + + def write_nsync_lmk_cfg_regs_to_eeprom(self, method): + """program the current LMK config to LMK eeprom""" + self._check_nsync_supported() + self.log.trace("LMK05318: store cfg in eeprom") + self._nsync_pll.write_cfg_regs_to_eeprom(method) + + def write_nsync_lmk_eeprom_to_cfg_regs(self): + """read register cfg from eeprom and store it into registers""" + self._check_nsync_supported() + self.log.trace("LMK05318: read cfg from eeprom") + self._nsync_pll.write_eeprom_to_cfg_regs() + + def get_nsync_lmk_eeprom_prog_cycles(self): + """ + returns the number of eeprom programming cycles + note: + the actual counter only increases after programming AND power-cycle/hard-reset + so multiple programming cycles without power cycle will lead to wrong + counter values + """ + self._check_nsync_supported() + return self._nsync_pll.get_eeprom_prog_cycles() + + def get_nsync_lmk_status_dpll(self): + """ + returns the DPLL status register as human readable string + """ + self._check_nsync_supported() + return self._nsync_pll.get_status_dpll() + + def get_nsync_lmk_status_pll_xo(self): + """ + returns the PLL and XO status register as human readable string + """ + self._check_nsync_supported() + return self._nsync_pll.get_status_pll_xo() + + def peek8(self, addr): + """Read from addr over SPI""" + self._check_nsync_supported() + val = self._nsync_pll.peek8(addr) + return val + + def poke8(self, addr, val, overwrite_mask=False): + """ + Write val to addr over SPI + Some register of the LMK IC are supposed not to be written and therefore + the whole register or just some bits. are protected by masking. + If you are really sure what you are doing you can overwrite the masking + by setting overwrite_mask=True + """ + self._check_nsync_supported() + self._nsync_pll.poke8(addr, val, overwrite_mask) + + def config_dpll(self, source): + """ + configures the dpll registers needed to lock to the expected signal + + Initial config files were created with TICSpro, then files were compared + against each other to determine which registers needed to be changed + """ + if source == self.SOURCE_NSYNC_LMK_PRI_GTY_RCV_CLK: + self._nsync_pll.pokes8(( + (0xC5,0x0B), + (0xCC,0x05), + (0xD1,0x08), + (0xD3,0x0A), + (0xD5,0x08), + (0xD7,0x0A), + (0xDA,0x02), + (0xDB,0xFA), + (0xDC,0xF1), + (0xDE,0x06), + (0xDF,0x1A), + (0xE0,0x81), + (0xE3,0x30), + (0xE4,0xD4), + (0xE6,0x06), + (0xE7,0x1A), + (0xE8,0x80), + (0x100,0x00), + (0x101,0x7D), + (0x103,0x08), + (0x109,0x0F), + (0x10A,0xA0), + (0x10F,0x78), + (0x110,0x00), + (0x111,0x00), + (0x112,0x00), + (0x113,0x0F), + (0x114,0x0E), + (0x115,0x0F), + (0x116,0x08), + (0x118,0x08), + (0x119,0x06), + (0x11A,0x08), + (0x11B,0x06), + (0x11E,0x00), + (0x11F,0x71), + (0x121,0xEB), + (0x123,0x09), + (0x128,0x03), + (0x129,0x05), + (0x12A,0x03), + (0x12D,0x3E), + (0x12E,0x3F), + (0x130,0x01), + (0x133,0x01), + (0x134,0x4D), + (0x135,0x55), + (0x136,0x55), + (0x137,0x55), + (0x138,0x55), + (0x139,0x55), + (0x13A,0xFF), + (0x13B,0xFF), + (0x13C,0xFF), + (0x13D,0xFF), + (0x13E,0xFF), + (0x141,0x19), + (0x145,0x78), + (0x147,0x00), + (0x148,0x27), + (0x149,0x10), + (0x14B,0x32), + (0x14F,0x78), + (0x151,0x00), + (0x152,0x27), + (0x153,0x10))) + elif source == self.SOURCE_NSYNC_LMK_PRI_FABRIC_CLK: + self._nsync_pll.pokes8(( + (0xC5,0x0D), + (0xCC,0x07), + (0xD1,0x04), + (0xD3,0x05), + (0xD5,0x04), + (0xD7,0x05), + (0xDA,0x01), + (0xDB,0x2C), + (0xDC,0x00), + (0xDE,0x03), + (0xDF,0x0D), + (0xE0,0x40), + (0xE3,0x18), + (0xE4,0x6A), + (0xE6,0x03), + (0xE7,0x0D), + (0xE8,0x40), + (0x100,0x06), + (0x101,0x00), + (0x103,0x7D), + (0x109,0xF4), + (0x10A,0x24), + (0x10F,0x7A), + (0x110,0x1F), + (0x111,0x1F), + (0x112,0x1F), + (0x113,0x13), + (0x114,0x10), + (0x115,0x13), + (0x116,0x04), + (0x118,0x04), + (0x119,0x02), + (0x11A,0x07), + (0x11B,0x02), + (0x11E,0x02), + (0x11F,0x6C), + (0x121,0xE7), + (0x123,0x25), + (0x128,0x00), + (0x129,0x06), + (0x12A,0x00), + (0x12D,0x17), + (0x12E,0x1B), + (0x130,0x00), + (0x133,0x1E), + (0x134,0x84), + (0x135,0x80), + (0x136,0x00), + (0x137,0x00), + (0x138,0x00), + (0x139,0x00), + (0x13A,0x00), + (0x13B,0x00), + (0x13C,0x00), + (0x13D,0x00), + (0x13E,0x00), + (0x141,0x0A), + (0x145,0x0A), + (0x147,0x03), + (0x148,0x0F), + (0x149,0x49), + (0x14B,0x14), + (0x14F,0x9A), + (0x151,0x03), + (0x152,0x0F), + (0x153,0x49))) + else: + raise RuntimeError( + "Invalid source for dpll programming") + + def set_ref_lock_led(self, val): + """ + Set the reference-locked LED on the back panel + """ + self._ref_lck_led.set(int(val)) diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_clk_mgr.py b/mpm/python/usrp_mpm/periph_manager/x4xx_clk_mgr.py new file mode 100644 index 000000000..bf845dd65 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_clk_mgr.py @@ -0,0 +1,780 @@ +# +# Copyright 2021 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +X400 Clocking Management + +This module handles the analog clocks on the X4x0 motherboard. The clocking +architecture of the motherboard is spread out between a clocking auxiliary board, +which contains a GPS-displicined OCXO, but also connects an external reference +to the motherboard. It also houses a PLL for deriving a clock from the network +(eCPRI). The clocking aux board has its own control class (ClockingAuxBrdControl) +which also contains controls for the eCPRI PLL. + +The motherboard itself has two main PLLs for clocking purposes: The Sample PLL +(also SPLL) is used to create all clocks used for RF-related purposes. It creates +the sample clock (a very fast clock, ~3 GHz) and the PLL reference clock (PRC) +which is used as a timebase for the daughterboard CPLD and a reference for the +LO synthesizers (50-64 MHz). + +Its input is the base reference clock (BRC). This clock comes either from the +clocking aux board, which in turn can provide a reference from the OCXO (with or +without GPS-disciplining) or from the external reference input SMA port. +The BRC is typically 10-25 MHz. + +The BRC can also come from the reference PLL (RPLL), when the clock source is +set to 'mboard'. The RPLL produces clocks that are consumed by the GTY banks +(for Ethernet and Aurora), but it can also generate a reference clock for +the SPLL. By default, its reference is a fixed 100 MHz clock, but it can also be +driven by the eCPRI PLL, which itself can be driven by a clock from the GTY banks, +which is the case if the clock source is set to 'nsync'. + +The master clock rate (MCR) is not actually controlled in this module, but it +depends on the sample clock rate. It also depends on the RFDC settings, so it is +controlled in x4xx.py, which has access to both RFDC and X4xxClockMgr. + +Block diagram (for more details, see the schematic):: + + ┌────────────────────────────────────────────────────────┐ + │ Clocking Aux Board │ + │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ + │ │ GPSDO ├─> OCXO │ │External│ │eCPRI/ │ │ + │ │ │ │ │ │ │ │nsync │ │ + │ └────────┘ └────┬───┘ └───┬────┘ └────────┘ │ + │ │ │ │ + │ │ │ │ + │ ┌───────────v─────────v───┐ ┌───────────┐ │ + │ │ │ │eCPRI PLL │ │ + │ └┐ MUX ┌┘ │LMK05318 │ │ + │ └─┐ ┌─┘ │ │ │ + │ └─┬─────────────────┘ └──┬────────┘ │ + │ │ │ │ + └───────────┼───────────────────────────┼────────────────┘ + │ │ + │ ┌─────────────┐ │ + ┌──v──v┐ │ │ + │ MUX │ │ │ ┌───── 100 MHz + └──┬───┘ │ │ │ + │Base Ref. Clock │ │ │ + ┌───────v───────┐ │ ┌───────v──v──┐ + │ Sample PLL │ └──┤Reference PLL│ + │ LMK04832 │ │LMK03328 │ + │ │ │ │ + │ │ │ │ + └──┬─────────┬──┘ └────┬────────┘ + │ │ │ + │ │ │ + v v v + Sample PLL Reference GTY Banks + Clock Clock + + +The code in this module controls the RPLL and SPLL as well as some of the muxing. +The eCPRI PLL is controlled from the ClockingAuxBrdControl class. +Most importantly, this class controls the sequencing of configuring clocks. This +means it won't only switch muxes and configure PLLs, but will also do things in +the right order, and put components in reset where necessary. +For this reason, it requires callbacks to reset RFDC and daughterboard clocks. +""" + +from enum import Enum +from usrp_mpm import lib # Pulls in everything from C++-land +from usrp_mpm.sys_utils import i2c_dev +from usrp_mpm.sys_utils.gpio import Gpio +from usrp_mpm.sys_utils.udev import dt_symbol_get_spidev +from usrp_mpm.periph_manager.x4xx_periphs import MboardRegsControl +from usrp_mpm.periph_manager.x4xx_sample_pll import LMK04832X4xx +from usrp_mpm.periph_manager.x4xx_reference_pll import LMK03328X4xx +from usrp_mpm.periph_manager.x4xx_clk_aux import ClockingAuxBrdControl +from usrp_mpm.mpmutils import poll_with_timeout +from usrp_mpm.rpc_server import no_rpc + +# this is not the frequency out of the GPSDO(GPS Lite, 20MHz) itself but +# the GPSDO on the CLKAUX board is used to fine tune the OCXO via EFC +# which is running at 10MHz +X400_GPSDO_OCXO_CLOCK_FREQ = 10e6 +X400_RPLL_I2C_LABEL = 'rpll_i2c' +X400_DEFAULT_RPLL_REF_SOURCE = '100M_reliable_clk' +X400_DEFAULT_MGT_CLOCK_RATE = 156.25e6 +X400_DEFAULT_INT_CLOCK_FREQ = 25e6 + +class X4xxClockMgr: + """ + X4x0 Clocking Manager + + The clocking subsystem of X4x0 is very complex. This class is designed to + capture all clocking-related logic specific to the X4x0. + + This class controls clock and time sources. + """ + CLOCK_SOURCE_MBOARD = "mboard" + CLOCK_SOURCE_INTERNAL = ClockingAuxBrdControl.SOURCE_INTERNAL + CLOCK_SOURCE_EXTERNAL = ClockingAuxBrdControl.SOURCE_EXTERNAL + CLOCK_SOURCE_GPSDO = ClockingAuxBrdControl.SOURCE_GPSDO + CLOCK_SOURCE_NSYNC = ClockingAuxBrdControl.SOURCE_NSYNC + + TIME_SOURCE_INTERNAL = "internal" + TIME_SOURCE_EXTERNAL = "external" + TIME_SOURCE_GPSDO = "gpsdo" + TIME_SOURCE_QSFP0 = "qsfp0" + + # All valid sync_sources for X4xx in the form of (clock_source, time_source) + valid_sync_sources = { + (CLOCK_SOURCE_MBOARD, TIME_SOURCE_INTERNAL), + (CLOCK_SOURCE_INTERNAL, TIME_SOURCE_INTERNAL), + (CLOCK_SOURCE_EXTERNAL, TIME_SOURCE_EXTERNAL), + (CLOCK_SOURCE_EXTERNAL, TIME_SOURCE_INTERNAL), + (CLOCK_SOURCE_GPSDO, TIME_SOURCE_GPSDO), + (CLOCK_SOURCE_GPSDO, TIME_SOURCE_INTERNAL), + (CLOCK_SOURCE_NSYNC, TIME_SOURCE_INTERNAL), + } + + class SetSyncRetVal(Enum): + OK = 'OK' + NOP = 'nop' + FAIL = 'failure' + + def __init__(self, + clock_source, + time_source, + ref_clock_freq, + sample_clock_freq, + is_legacy_mode, + clk_aux_board, + cpld_control, + log): + # Store parent objects + self.log = log.getChild("ClkMgr") + self._cpld_control = cpld_control + self._clocking_auxbrd = clk_aux_board + self._time_source = time_source + self._clock_source = clock_source + self._int_clock_freq = X400_DEFAULT_INT_CLOCK_FREQ + self._ext_clock_freq = ref_clock_freq + # Preallocate other objects to satisfy linter + self.mboard_regs_control = None + self._sample_pll = None + self._reference_pll = None + self._rpll_i2c_bus = None + self._base_ref_clk_select = None + self._set_reset_rfdc = lambda **kwargs: None + self._set_reset_db_clocks = lambda *args: None + self._rpll_reference_sources = {} + # Init peripherals + self._init_available_srcs() + self._init_clk_peripherals() + # Now initialize the clocks themselves + self._init_ref_clock_and_time( + clock_source, + ref_clock_freq, + sample_clock_freq, + is_legacy_mode, + ) + self._init_meas_clock() + self._cpld_control.enable_pll_ref_clk() + + ########################################################################### + # Initialization code + ########################################################################### + def _init_available_srcs(self): + """ + Initialize the available clock and time sources. + """ + has_gps = self._clocking_auxbrd and self._clocking_auxbrd.is_gps_supported() + self._avail_clk_sources = [self.CLOCK_SOURCE_MBOARD] + if self._clocking_auxbrd: + self._avail_clk_sources.extend([ + self.CLOCK_SOURCE_INTERNAL, + self.CLOCK_SOURCE_EXTERNAL]) + if self._clocking_auxbrd.is_nsync_supported(): + self._avail_clk_sources.append(self.CLOCK_SOURCE_NSYNC) + if has_gps: + self._avail_clk_sources.append(self.CLOCK_SOURCE_GPSDO) + self.log.trace(f"Available clock sources are: {self._avail_clk_sources}") + self._avail_time_sources = [ + self.TIME_SOURCE_INTERNAL, self.TIME_SOURCE_EXTERNAL, self.TIME_SOURCE_QSFP0] + if has_gps: + self._avail_time_sources.append(self.TIME_SOURCE_GPSDO) + self.log.trace("Available time sources are: {}".format(self._avail_time_sources)) + + def _init_clk_peripherals(self): + """ + Initialize objects for peripherals owned by this class. Most importantly, + this includes the RPLL and SPLL control classes. + """ + # Create SPI and I2C interfaces to the LMK registers + spll_spi_node = dt_symbol_get_spidev('spll') + sample_lmk_regs_iface = lib.spi.make_spidev_regs_iface( + spll_spi_node, + 1000000, # Speed (Hz) + 0x3, # SPI mode + 8, # Addr shift + 0, # Data shift + 1<<23, # Read flag + 0, # Write flag + ) + # Initialize I2C connection to RPLL + self._rpll_i2c_bus = i2c_dev.dt_symbol_get_i2c_bus(X400_RPLL_I2C_LABEL) + if self._rpll_i2c_bus is None: + raise RuntimeError("RPLL I2C bus could not be found") + reference_lmk_regs_iface = lib.i2c.make_i2cdev_regs_iface( + self._rpll_i2c_bus, + 0x54, # addr + False, # ten_bit_addr + 100, # timeout_ms + 1 # reg_addr_size + ) + self._sample_pll = LMK04832X4xx(sample_lmk_regs_iface, self.log) + self._reference_pll = LMK03328X4xx(reference_lmk_regs_iface, self.log) + # Init BRC select GPIO control + self._base_ref_clk_select = Gpio('BASE-REFERENCE-CLOCK-SELECT', Gpio.OUTPUT, 1) + + def _init_ref_clock_and_time(self, + clock_source, + ref_clock_freq, + sample_clock_freq, + is_legacy_mode, + ): + """ + Initialize clock and time sources. After this function returns, the + reference signals going to the FPGA are valid. + + This is only called once, during __init__(). Calling it again will set + clocks to defaults, but is also redundant since clocks do not need to be + initialized twice. + """ + # A dictionary of tuples (source #, rate) corresponding to each + # available RPLL reference source. + # source # 1 => PRIREF source + # source # 2 => SECREF source + self._rpll_reference_sources = {X400_DEFAULT_RPLL_REF_SOURCE: (2, 100e6)} + reference_rates = [None, None] + for source, rate in self._rpll_reference_sources.values(): + reference_rates[source-1] = rate + self._reference_pll.reference_rates = reference_rates + # Now initializes and reconfigure all clocks. + # If clock_source and ref_clock_freq are not provided, they will not be changed. + # If any other parameters are not provided, they will be configured with + # default values. + self._reset_clocks(value=True, reset_list=['cpld']) + if clock_source is not None: + self._set_brc_source(clock_source) + if ref_clock_freq is not None: + self._set_ref_clock_freq(ref_clock_freq) + self._config_rpll( + X400_DEFAULT_MGT_CLOCK_RATE, + X400_DEFAULT_INT_CLOCK_FREQ, + X400_DEFAULT_RPLL_REF_SOURCE) + self._config_spll(sample_clock_freq, is_legacy_mode) + self._reset_clocks(value=False, reset_list=['cpld']) + + def _init_meas_clock(self): + """ + Initialize the TDC measurement clock. After this function returns, the + FPGA TDC meas_clock is valid. + """ + # This may or may not be used for X400. Leave as a place holder + self.log.debug("TDC measurement clock not yet implemented.") + + ########################################################################### + # Public APIs (that are not exposed as MPM calls) + ########################################################################### + @no_rpc + def set_rfdc_reset_cb(self, rfdc_reset_cb): + """ + Set reference to RFDC control. Ideally, we'd get that in __init__(), but + due to order of operations, it's not ready yet when we call that. + """ + self._set_reset_rfdc = rfdc_reset_cb + + @no_rpc + def set_dboard_reset_cb(self, db_reset_cb): + """ + Set reference to RFDC control. Ideally, we'd get that in __init__(), but + due to order of operations, it's not ready yet when we call that. + """ + self._set_reset_db_clocks = db_reset_cb + + @no_rpc + def unset_cbs(self): + """ + Removes any stored references to our owning X4xx class instance + """ + self._set_reset_rfdc = None + self._set_reset_db_clocks = None + + @no_rpc + def config_pps_to_timekeeper(self, master_clock_rate): + """ Configures the path from the PPS to the timekeeper""" + pps_source = "internal_pps" \ + if self._time_source == self.TIME_SOURCE_INTERNAL \ + else "external_pps" + self._sync_spll_clocks(pps_source) + self._configure_pps_forwarding(True, master_clock_rate) + + @no_rpc + def get_clock_sources(self): + """ + Lists all available clock sources. + """ + return self._avail_clk_sources + + @no_rpc + def get_time_sources(self): + " Returns list of valid time sources " + return self._avail_time_sources + + @no_rpc + def get_ref_clock_freq(self): + " Returns the currently active reference clock frequency (BRC) " + clock_source = self.get_clock_source() + if clock_source == self.CLOCK_SOURCE_MBOARD: + return self._int_clock_freq + if clock_source == self.CLOCK_SOURCE_GPSDO: + return X400_GPSDO_OCXO_CLOCK_FREQ + # clock_source == "external": + return self._ext_clock_freq + + @no_rpc + def get_ref_locked(self): + """ + Return lock status both RPLL and SPLL. + """ + ref_pll_status = self._reference_pll.get_status() + sample_pll_status = self._sample_pll.get_status() + return all([ + ref_pll_status['PLL1 lock'], + ref_pll_status['PLL2 lock'], + sample_pll_status['PLL1 lock'], + sample_pll_status['PLL2 lock'], + ]) + + @no_rpc + def set_spll_rate(self, sample_clock_freq, is_legacy_mode): + """ + Safely set the output rate of the sample PLL. + + This will do the required resets. + """ + self._reset_clocks(value=True, reset_list=('rfdc', 'cpld', 'db_clock')) + self._config_spll(sample_clock_freq, is_legacy_mode) + self._reset_clocks(value=False, reset_list=('rfdc', 'cpld', 'db_clock')) + + @no_rpc + def set_sync_source(self, clock_source, time_source): + """ + Selects reference clock and PPS sources. Unconditionally re-applies the + time source to ensure continuity between the reference clock and time + rates. + Note that if we change the source such that the time source is changed + to 'external', then we need to also disable exporting the reference + clock (RefOut and PPS-In are the same SMA connector). + """ + assert (clock_source, time_source) in self.valid_sync_sources, \ + f'Clock and time source pair ({clock_source}, {time_source}) is ' \ + 'not a valid selection' + # Now see if we can keep the current settings, or if we need to run an + # update of sync sources: + if (clock_source == self._clock_source) and (time_source == self._time_source): + spll_status = self._sample_pll.get_status() + rpll_status = self._reference_pll.get_status() + if (spll_status['PLL1 lock'] and spll_status['PLL2 lock'] and + rpll_status['PLL1 lock'] and rpll_status['PLL2 lock']): + # Nothing change no need to do anything + self.log.trace("New sync source assignment matches " + "previous assignment. Ignoring update command.") + return self.SetSyncRetVal.NOP + self.log.debug( + "Although the clock source has not changed, some PLLs " + "are not locked. Setting clock source again...") + self.log.trace("- SPLL status: {}".format(spll_status)) + self.log.trace("- RPLL status: {}".format(rpll_status)) + # Start setting sync source + self.log.debug( + f"Setting sync source to time_source={time_source}, " + f"clock_source={clock_source}") + self._time_source = time_source + # Reset downstream clocks (excluding RPLL) + self._reset_clocks(value=True, reset_list=('db_clock', 'cpld', 'rfdc', 'spll')) + self._set_brc_source(clock_source) + return self.SetSyncRetVal.OK + + @no_rpc + def set_clock_source_out(self, enable=True): + """ + Allows routing the clock configured as source on the clk aux board to + the RefOut terminal. This only applies to internal, gpsdo and nsync. + """ + clock_source = self.get_clock_source() + if self.get_time_source() == self.TIME_SOURCE_EXTERNAL: + raise RuntimeError( + 'Cannot export clock when using external time reference!') + if clock_source not in self._clocking_auxbrd.VALID_CLK_EXPORTS: + raise RuntimeError(f"Invalid source to export: `{clock_source}'") + if self._clocking_auxbrd is None: + raise RuntimeError("No clocking aux board available") + return self._clocking_auxbrd.export_clock(enable) + + ########################################################################### + # Top-level BIST APIs + # + # These calls will be available as MPM calls. They are only needed by BIST. + ########################################################################### + def enable_ecpri_clocks(self, enable=True, clock='both'): + """ + Enable or disable the export of FABRIC and GTY_RCV eCPRI + clocks. Main use case until we support eCPRI is manufacturing + testing. + """ + self.mboard_regs_control.enable_ecpri_clocks(enable, clock) + + def nsync_change_input_source(self, source): + """ + Switches the input reference source of the clkaux lmk (the "eCPRI PLL"). + + Valid options are: fabric_clk, gty_rcv_clk, and sec_ref. + + fabric_clk and gty_rcv_clk are clock sources from the mboard. + They are both inputs to the primary reference source of the clkaux lmk. + sec_ref is the default reference select for the clkaux lmk, it has + two inputs: Ref in or internal and GPS mode + + Only a public API for the BIST. + """ + assert source in ( + self._clocking_auxbrd.SOURCE_NSYNC_LMK_PRI_FABRIC_CLK, + self._clocking_auxbrd.SOURCE_NSYNC_LMK_PRI_GTY_RCV_CLK, + self._clocking_auxbrd.NSYNC_SEC_REF, + ) + if source == self._clocking_auxbrd.SOURCE_NSYNC_LMK_PRI_FABRIC_CLK: + self.enable_ecpri_clocks(True, 'fabric') + self._clocking_auxbrd.set_nsync_ref_select( + self._clocking_auxbrd.NSYNC_PRI_REF) + self._clocking_auxbrd.set_nsync_pri_ref_source(source) + elif source == self._clocking_auxbrd.SOURCE_NSYNC_LMK_PRI_GTY_RCV_CLK: + self.enable_ecpri_clocks(True, 'gty_rcv') + self._clocking_auxbrd.set_nsync_ref_select( + self._clocking_auxbrd.NSYNC_PRI_REF) + self._clocking_auxbrd.set_nsync_pri_ref_source(source) + else: + self._clocking_auxbrd.set_nsync_ref_select( + self._clocking_auxbrd.NSYNC_SEC_REF) + + def config_rpll_to_nsync(self): + """ + Configures the rpll to use the LMK28PRIRefClk output + by the clkaux LMK + """ + # LMK28PRIRefClk only available when nsync is source, as lmk + # is powered off otherwise + self.set_sync_source(clock_source='nsync', time_source=self._time_source) + + # Add LMK28PRIRefClk as an available RPLL reference source + # 1 => PRIREF source; source is output at 25 MHz + # TODO: enable out4 on LMK + previous_ref_rate = self._reference_pll.reference_rates[0] + self._rpll_reference_sources['clkaux_nsync_clk'] = (1, 25e6) + self._reference_pll.reference_rates[0] = 25e6 + self._config_rpll(X400_DEFAULT_MGT_CLOCK_RATE, + X400_DEFAULT_INT_CLOCK_FREQ, + 'clkaux_nsync_clk') + + # remove clkaux_nsync_clk as a valid reference source for later calls + # to _config_rpll(), it is only valid in this configuration + self._reference_pll.reference_rates[0] = previous_ref_rate + del self._rpll_reference_sources['clkaux_nsync_clk'] + + def get_fpga_aux_ref_freq(self): + """ + Return the tick count of an FPGA counter which measures the width of + the PPS signal on the FPGA_AUX_REF FPGA input using a 40 MHz clock. + Main use case until we support eCPRI is manufacturing testing. + A return value of 0 indicates absence of a valid PPS signal on the + FPGA_AUX_REF line. + """ + return self.mboard_regs_control.get_fpga_aux_ref_freq() + + ########################################################################### + # Top-level APIs + # + # These calls will be available as MPM calls + ########################################################################### + def get_clock_source(self): + " Return the currently selected clock source " + return self._clock_source + + def get_time_source(self): + " Return the currently selected time source " + return self._time_source + + def get_spll_freq(self): + """ Returns the output frequency setting of the SPLL """ + return self._sample_pll.output_freq + + def get_prc_rate(self): + """ + Returns the rate of the PLL Reference Clock (PRC) which is + routed to the daughterboards. + Note: The ref clock will change if the sample clock frequency + is modified. + """ + prc_clock_map = { + 2.94912e9: 61.44e6, + 3e9: 62.5e6, + # 3e9: 50e6, RF Legacy mode will be checked separately + 3.072e9: 64e6, + } + + # RF Legacy Mode always has a PRC rate of 50 MHz + if self._sample_pll.is_legacy_mode: + return 50e6 + # else: + return prc_clock_map[self.get_spll_freq()] + + def set_ref_clk_tuning_word(self, tuning_word, out_select=0): + """ + Set the tuning word for the clocking aux board DAC. This wull update the + tuning word used by the DAC. + """ + if self._clocking_auxbrd is not None: + self._clocking_auxbrd.config_dac(tuning_word, out_select) + else: + raise RuntimeError("No clocking aux board available") + + def get_ref_clk_tuning_word(self, out_select=0): + """ + Get the tuning word configured for the clocking aux board DAC. + """ + if self._clocking_auxbrd is None: + raise RuntimeError("No clocking aux board available") + return self._clocking_auxbrd.read_dac(out_select) + + def store_ref_clk_tuning_word(self, tuning_word): + """ + Store the given tuning word in the clocking aux board ID EEPROM. + """ + if self._clocking_auxbrd is not None: + self._clocking_auxbrd.store_tuning_word(tuning_word) + else: + raise RuntimeError("No clocking aux board available") + + def get_sync_sources(self): + """ + Enumerates permissible sync sources. + """ + return [{ + "time_source": time_source, + "clock_source": clock_source + } for (clock_source, time_source) in self.valid_sync_sources] + + + ########################################################################### + # Low-level controls + ########################################################################### + def _reset_clocks(self, value, reset_list): + """ + Shuts down all clocks downstream to upstream or clears reset on all + clocks upstream to downstream. Specify the list of clocks to reset in + reset_list. The order of clocks specified in the reset_list does not + affect the order in which the clocks are reset. + """ + if value: + self.log.trace("Reset clocks: {}".format(reset_list)) + if 'db_clock' in reset_list: + self._set_reset_db_clocks(value) + if 'cpld' in reset_list: + self._cpld_control.enable_pll_ref_clk(enable=False) + if 'rfdc' in reset_list: + self._set_reset_rfdc(reset=True) + if 'spll' in reset_list: + self._sample_pll.reset(value, hard=True) + if 'rpll' in reset_list: + self._reference_pll.reset(value, hard=True) + else: + self.log.trace("Bring clocks out of reset: {}".format(reset_list)) + if 'rpll' in reset_list: + self._reference_pll.reset(value, hard=True) + if 'spll' in reset_list: + self._sample_pll.reset(value, hard=True) + if 'rfdc' in reset_list: + self._set_reset_rfdc(reset=False) + if 'cpld' in reset_list: + self._cpld_control.enable_pll_ref_clk(enable=True) + if 'db_clock' in reset_list: + self._set_reset_db_clocks(value) + + def _config_rpll(self, usr_clk_rate, internal_brc_rate, internal_brc_source): + """ + Configures the LMK03328 to generate the desired MGT reference clocks + and internal BRC rate. + + Currently, the MGT protocol selection is not supported, but a custom + usr_clk_rate can be generated from PLL1. + + usr_clk_rate - the custom clock rate to generate from PLL1 + internal_brc_rate - the rate to output as the BRC + internal_brc_source - the reference source which drives the RPLL + """ + if internal_brc_source not in self._rpll_reference_sources: + self.log.error('Invalid internal BRC source of {} was selected.' + .format(internal_brc_source)) + raise RuntimeError('Invalid internal BRC source of {} was selected.' + .format(internal_brc_source)) + ref_select = self._rpll_reference_sources[internal_brc_source][0] + + # If the desired rate matches the rate of the primary reference source, + # directly passthrough that reference source + if internal_brc_rate == self._reference_pll.reference_rates[0]: + brc_select = 'bypass' + else: + brc_select = 'PLL' + self._reference_pll.init() + self._reference_pll.config( + ref_select, internal_brc_rate, usr_clk_rate, brc_select) + # The internal BRC rate will only change when _config_rpll is called + # with a new internal BRC rate + self._int_clock_freq = internal_brc_rate + + def _config_spll(self, sample_clock_freq, is_legacy_mode): + """ + Configures the SPLL for the specified master clock rate. + """ + self._sample_pll.init() + self._sample_pll.config(sample_clock_freq, self.get_ref_clock_freq(), + is_legacy_mode) + + def _set_brc_source(self, clock_source): + """ + Switches the Base Reference Clock Source between internal, external, + mboard, and gpsdo using the GPIO pin and clocking aux board control. + internal is a clock source internal to the clocking aux board, but + external to the motherboard. + Should not be called outside of set_sync_source or _init_ref_clock_and_time + without proper reset and reconfig of downstream clocks. + """ + if clock_source == self.CLOCK_SOURCE_MBOARD: + self._base_ref_clk_select.set(1) + if self._clocking_auxbrd: + self._clocking_auxbrd.export_clock(False) + else: + if self._clocking_auxbrd is None: + self.log.error('Invalid BRC selection {}. No clocking aux ' + 'board was found.'.format(clock_source)) + raise RuntimeError('Invalid BRC selection {}'.format(clock_source)) + self._base_ref_clk_select.set(0) + if clock_source == self.CLOCK_SOURCE_EXTERNAL: + # This case is a bit special: We also need to tell the clocking + # aux board if we plan to consume the external time reference or + # not. + time_src_board = \ + ClockingAuxBrdControl.SOURCE_EXTERNAL \ + if self._time_source == self.TIME_SOURCE_EXTERNAL \ + else ClockingAuxBrdControl.SOURCE_INTERNAL + self._clocking_auxbrd.set_source( + ClockingAuxBrdControl.SOURCE_EXTERNAL, time_src_board) + elif clock_source == self.CLOCK_SOURCE_INTERNAL: + self._clocking_auxbrd.set_source(ClockingAuxBrdControl.SOURCE_INTERNAL) + elif clock_source == self.CLOCK_SOURCE_GPSDO: + self._clocking_auxbrd.set_source(ClockingAuxBrdControl.SOURCE_GPSDO) + elif clock_source == self.CLOCK_SOURCE_NSYNC: + self._clocking_auxbrd.set_source(ClockingAuxBrdControl.SOURCE_NSYNC) + else: + self.log.error('Invalid BRC selection {}'.format(clock_source)) + raise RuntimeError('Invalid BRC selection {}'.format(clock_source)) + self._clock_source = clock_source + self.log.debug(f"Base reference clock source is: {clock_source}") + + def _sync_spll_clocks(self, pps_source="internal_pps"): + """ + Synchronize base reference clock (BRC) and PLL reference clock (PRC) + at start of PPS trigger. + + Uses the LMK 04832 pll1_r_divider_sync to synchronize BRC with PRC. + This sync method uses a callback to actual trigger the sync. Before + the trigger is pulled (CLOCK_CTRL_PLL_SYNC_TRIGGER) PPS source is + configured base on current reference clock and pps_source. After sync + trigger the method waits for 1sec for the CLOCK_CTRL_PLL_SYNC_DONE + to go high. + + :param pps_source: select whether internal ("internal_pps") or external + ("external_pps") PPS should be used. This parameter + is taken into account when the current clock source + is external. If the current clock source is set to + internal then this parameter is not taken into + account. + :return: success state of sync call + :raises RuntimeError: for invalid combinations of reference clock and + pps_source + """ + def select_pps(): + """ + Select PPS source based on current clock source and pps_source. + + This returns the bits for the motherboard CLOCK_CTRL register that + control the PPS source. + """ + EXT_PPS = "external_pps" + INT_PPS = "internal_pps" + PPS_SOURCES = (EXT_PPS, INT_PPS) + assert pps_source in PPS_SOURCES, \ + "%s not in %s" % (pps_source, PPS_SOURCES) + + supported_configs = { + (10E6, EXT_PPS): MboardRegsControl.CLOCK_CTRL_PPS_EXT, + (10E6, INT_PPS): MboardRegsControl.CLOCK_CTRL_PPS_INT_10MHz, + (25E6, INT_PPS): MboardRegsControl.CLOCK_CTRL_PPS_INT_25MHz + } + + config = (self.get_ref_clock_freq(), pps_source) + if config not in supported_configs: + raise RuntimeError("Unsupported combination of reference clock " + "(%.2E) and PPS source (%s) for PPS sync." % + config) + return supported_configs[config] + + return self._sample_pll.pll1_r_divider_sync( + lambda: self.mboard_regs_control.pll_sync_trigger(select_pps())) + + def _configure_pps_forwarding(self, enable, master_clock_rate, delay=1.0): + """ + Configures the PPS forwarding to the sample clock domain (master + clock rate). This function assumes _sync_spll_clocks function has + already been executed. + + :param enable: Boolean to choose whether PPS is forwarded to the + sample clock domain. + + :param delay: Delay in seconds from the PPS rising edge to the edge + occurence in the application. This value has to be in + range 0 < x <= 1. In order to forward the PPS signal + from base reference clock to sample clock an aligned + rising edge of the clock is required. This can be + created by the _sync_spll_clocks function. Based on the + greatest common divisor of the two clock rates there + are multiple occurences of an aligned edge each second. + One of these aligned edges has to be chosen for the + PPS forwarding by setting this parameter. + + :return: None, Exception on error + """ + return self.mboard_regs_control.configure_pps_forwarding( + enable, master_clock_rate, self.get_prc_rate(), delay) + + def _set_ref_clock_freq(self, freq): + """ + Tell our USRP what the frequency of the external reference clock is. + + Will throw if it's not a valid value. + """ + if (freq < 1e6) or (freq > 50e6): + raise RuntimeError('External reference clock frequency is out of the valid range.') + if (freq % 40e3) != 0: + # TODO: implement exception of a 50e3 step size for 200MSPS + raise RuntimeError('External reference clock frequency is of incorrect step size.') + self._ext_clock_freq = freq + # If the external source is currently selected we also need to re-apply the + # time_source. This call also updates the dboards' rates. + if self.get_clock_source() == self.CLOCK_SOURCE_EXTERNAL: + self.set_sync_source(self._clock_source, self._time_source) diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_gps_mgr.py b/mpm/python/usrp_mpm/periph_manager/x4xx_gps_mgr.py new file mode 100644 index 000000000..25a91c19b --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_gps_mgr.py @@ -0,0 +1,157 @@ +# +# Copyright 2021 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +X4XX GPS Manager + +Handles GPS-related tasks +""" + +import re +from usrp_mpm.gpsd_iface import GPSDIfaceExtension + +class X4xxGPSMgr: + """ + Manager class for GPS-related actions for the X4XX. + + This also "disables" the sensors when the GPS is not enabled. + """ + def __init__(self, clk_aux_board, log): + assert clk_aux_board and clk_aux_board.is_gps_supported() + self._clocking_auxbrd = clk_aux_board + self.log = log.getChild('GPS') + self.log.trace("Initializing GPSd interface") + self._gpsd = GPSDIfaceExtension() + # To disable sensors, we simply return an empty value if GPS is disabled. + # For TPV, SKY, and GPGGA, we can do this in the same fashion (they are + # very similar). gps_time is different (it returns an int) so for sake + # of simplicity it's defined separately below. + for sensor_name in ('gps_tpv', 'gps_sky', 'gps_gpgga'): + sensor_api = f'get_{sensor_name}_sensor' + setattr( + self, sensor_api, + lambda sensor_name=sensor_name: { + 'name': sensor_name, 'type': 'STRING', + 'unit': '', 'value': 'n/a'} \ + if not self.is_gps_enabled() \ + else getattr(self._gpsd, f'get_{sensor_name}_sensor')() + ) + + def extend(self, context): + """ + Extend 'context' with the sensor methods of this class (get_gps_*_sensor). + If 'context' already has such a method, it is skipped. + + Returns a dictionary compatible to mboard_sensor_callback_map. + """ + new_methods = { + re.search(r"get_(.*)_sensor", method_name).group(1): method_name + for method_name in dir(self) + if not method_name.startswith('_') \ + and callable(getattr(self, method_name)) \ + and method_name.endswith("sensor")} + for method_name in new_methods.values(): + if hasattr(context, method_name): + continue + new_method = getattr(self, method_name) + self.log.trace("%s: Adding %s method", context, method_name) + setattr(context, method_name, new_method) + return new_methods + + def is_gps_enabled(self): + """ + Return True if the GPS is enabled/active. + """ + return self._clocking_auxbrd.is_gps_enabled() + + def get_gps_enabled_sensor(self): + """ + Get enabled status of GPS as a sensor dict + """ + gps_enabled = self.is_gps_enabled() + return { + 'name': 'gps_enabled', + 'type': 'BOOLEAN', + 'unit': 'enabled' if gps_enabled else 'disabled', + 'value': str(gps_enabled).lower(), + } + + def get_gps_locked_sensor(self): + """ + Get lock status of GPS as a sensor dict + """ + gps_locked = self.is_gps_enabled() and \ + bool(self._clocking_auxbrd.get_gps_lock()) + return { + 'name': 'gps_lock', + 'type': 'BOOLEAN', + 'unit': 'locked' if gps_locked else 'unlocked', + 'value': str(gps_locked).lower(), + } + + def get_gps_alarm_sensor(self): + """ + Get alarm status of GPS as a sensor dict + """ + gps_alarm = self.is_gps_enabled() and \ + bool(self._clocking_auxbrd.get_gps_alarm()) + return { + 'name': 'gps_alarm', + 'type': 'BOOLEAN', + 'unit': 'active' if gps_alarm else 'not active', + 'value': str(gps_alarm).lower(), + } + + def get_gps_warmup_sensor(self): + """ + Get warmup status of GPS as a sensor dict + """ + gps_warmup = self.is_gps_enabled() and \ + bool(self._clocking_auxbrd.get_gps_warmup()) + return { + 'name': 'gps_warmup', + 'type': 'BOOLEAN', + 'unit': 'warming up' if gps_warmup else 'warmup done', + 'value': str(gps_warmup).lower(), + } + + def get_gps_survey_sensor(self): + """ + Get survey status of GPS as a sensor dict + """ + gps_survey = self.is_gps_enabled() and \ + bool(self._clocking_auxbrd.get_gps_survey()) + return { + 'name': 'gps_survey', + 'type': 'BOOLEAN', + 'unit': 'survey active' if gps_survey else 'survey not active', + 'value': str(gps_survey).lower(), + } + + def get_gps_phase_lock_sensor(self): + """ + Get phase_lock status of GPS as a sensor dict + """ + gps_phase_lock = self.is_gps_enabled() and \ + bool(self._clocking_auxbrd.get_gps_phase_lock()) + return { + 'name': 'gps_phase_lock', + 'type': 'BOOLEAN', + 'unit': 'phase locked' if gps_phase_lock else 'no phase lock', + 'value': str(gps_phase_lock).lower(), + } + + def get_gps_time_sensor(self): + """ + + """ + if not self.is_gps_enabled(): + return { + 'name': 'gps_time', + 'type': 'INTEGER', + 'unit': 'seconds', + 'value': str(-1), + } + return self._gpsd.get_gps_time_sensor() diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py b/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py new file mode 100644 index 000000000..927db3360 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py @@ -0,0 +1,204 @@ +# +# Copyright 2021 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +X4xx motherboard CPLD control +""" + +from usrp_mpm import lib # Pulls in everything from C++-land + +def parse_encoded_git_hash(encoded): + git_hash = encoded & 0x0FFFFFFF + tree_dirty = ((encoded & 0xF0000000) > 0) + dirtiness_qualifier = 'dirty' if tree_dirty else 'clean' + return (git_hash, dirtiness_qualifier) + +class MboardCPLD: + """ + Control for the CPLD. + """ + # pylint: disable=bad-whitespace + SIGNATURE_OFFSET = 0x0000 + COMPAT_REV_OFFSET = 0x0004 + OLDEST_COMPAT_REV_OFFSET = 0x0008 + GIT_HASH_OFFSET = 0x0010 + DB_ENABLE_OFFSET = 0x0020 + SERIAL_NO_LO_OFFSET = 0x0034 + SERIAL_NO_HI_OFFSET = 0x0038 + CMI_OFFSET = 0x003C + + # change these revisions only on breaking changes + OLDEST_REQ_COMPAT_REV = 0x20122114 + REQ_COMPAT_REV = 0x20122114 + SIGNATURE = 0x0A522D27 + PLL_REF_CLOCK_ENABLED = 1 << 2 + ENABLE_CLK_DB0 = 1 << 8 + ENABLE_CLK_DB1 = 1 << 9 + ENABLE_PRC = 1 << 10 + DISABLE_CLK_DB0 = 1 << 12 + DISABLE_CLK_DB1 = 1 << 13 + DISABLE_PRC = 1 << 14 + RELEASE_RST_DB0 = 1 << 16 + RELEASE_RST_DB1 = 1 << 17 + ASSERT_RST_DB0 = 1 << 20 + ASSERT_RST_DB1 = 1 << 21 + # pylint: enable=bad-whitespace + + def __init__(self, spi_dev_node, log): + self.log = log.getChild("CPLD") + self.regs = lib.spi.make_spidev_regs_iface( + spi_dev_node, + 1000000, # Speed (Hz) + 0, # SPI mode + 32, # Addr shift + 0, # Data shift + 0, # Read flag + 1<<47 # Write flag + ) + self.poke32 = self.regs.poke32 + self.peek32 = self.regs.peek32 + + def enable_pll_ref_clk(self, enable=True): + """ + Enables or disables the PLL reference clock. + + This makes no assumptions on the prior state of the clock. It does check + if the clock-enable was successful by polling the CPLD, and throws if + not. + """ + def check_pll_enabled(): + return self.peek32(self.DB_ENABLE_OFFSET) & self.PLL_REF_CLOCK_ENABLED + if enable: + self.poke32(self.DB_ENABLE_OFFSET, self.ENABLE_PRC) + if not check_pll_enabled(): + self.log.error("PRC enable failed!") + raise RuntimeError('PRC enable failed!') + return + # Disable PRC: + self.poke32(self.DB_ENABLE_OFFSET, self.DISABLE_PRC) + if check_pll_enabled(): + self.log.error('PRC reset failed!') + raise RuntimeError('PRC reset failed!') + + def enable_daughterboard(self, db_id, enable=True): + """ Enable or disable clock forwarding to a given DB """ + if db_id == 0: + release_reset = self.RELEASE_RST_DB0 + assert_reset = self.ASSERT_RST_DB0 + else: + release_reset = self.RELEASE_RST_DB1 + assert_reset = self.ASSERT_RST_DB1 + value = self.peek32(self.DB_ENABLE_OFFSET) + if enable: + # De-assert reset + value = (value | release_reset) & (~assert_reset) + else: #disable + # Assert reset + value = (value | assert_reset) & (~release_reset) + self.poke32(self.DB_ENABLE_OFFSET, value) + + def enable_daughterboard_support_clock(self, db_id, enable=True): + """ Enable or disable clock forwarding to a given DB """ + if db_id == 0: + clk_enable = self.ENABLE_CLK_DB0 + clk_disable = self.DISABLE_CLK_DB0 + else: + clk_enable = self.ENABLE_CLK_DB1 + clk_disable = self.DISABLE_CLK_DB1 + value = self.peek32(self.DB_ENABLE_OFFSET) + if enable: + # Enable clock + value = (value | clk_enable) & (~clk_disable) + else: #disable + # Disable clock + value = (value | clk_disable) & (~clk_enable) + self.poke32(self.DB_ENABLE_OFFSET, value) + + def check_signature(self): + """ + Assert that the CPLD signature is correct. If the CPLD 'signature' + register returns something unexpectected, throws a RuntimeError. + """ + read_signature = self.peek32(self.SIGNATURE_OFFSET) + if self.SIGNATURE != read_signature: + self.log.error('MB PS CPLD signature {:X} does not match ' + 'expected value {:X}'.format(read_signature, self.SIGNATURE)) + raise RuntimeError('MB PS CPLD signature {:X} does not match ' + 'expected value {:X}'.format(read_signature, self.SIGNATURE)) + + def check_compat_version(self): + """ + Check oldest compatible revision offset of HW against required revision. + The value has to match as the register offsets depends on them. + Furthermore there needs to be a minimum revision to check for existence + of functionality. + """ + cpld_image_compat_revision = self.peek32(self.OLDEST_COMPAT_REV_OFFSET) + if cpld_image_compat_revision < self.OLDEST_REQ_COMPAT_REV: + error_message = ( + 'MB CPLD oldest compatible revision' + f' 0x{cpld_image_compat_revision:08x} is out of date. Update' + f' your CPLD image to 0x{self.OLDEST_REQ_COMPAT_REV:08x}.') + self.log.error(error_message) + raise RuntimeError(error_message) + if cpld_image_compat_revision > self.OLDEST_REQ_COMPAT_REV: + error_message = ( + 'MB CPLD oldest compatible revision' + f' 0x{cpld_image_compat_revision:08x} is unknown. Downgrade' + f' your CPLD image to 0x{self.OLDEST_REQ_COMPAT_REV:08x}.') + self.log.error(error_message) + raise RuntimeError(error_message) + + if not self.has_compat_version(self.REQ_COMPAT_REV): + error_message = ( + "MB CPLD compatible revision is too old. Update your CPLD" + f" image to at least 0x{self.REQ_COMPAT_REV:08x}.") + self.log.error(error_message) + raise RuntimeError(error_message) + + def has_compat_version(self, min_required_version): + """ + Check for a minimum required version. + """ + if min_required_version < self.REQ_COMPAT_REV: + self.log.warning( + "Somebody called MB CPLD has_compat_version with revision" + f" 0x{min_required_version:x} which is older than the mandated" + f" version 0x{self.REQ_COMPAT_REV:x}.") + cpld_image_compat_revision = self.peek32(self.COMPAT_REV_OFFSET) + return cpld_image_compat_revision >= min_required_version + + def trace_git_hash(self): + """ + Trace build of MB CPLD + """ + git_hash_rb = self.peek32(self.GIT_HASH_OFFSET) + (git_hash, dirtiness_qualifier) = parse_encoded_git_hash(git_hash_rb) + self.log.trace("MB CPLD build GIT Hash: {:07x} ({})".format( + git_hash, dirtiness_qualifier)) + + def set_serial_number(self, serial_number): + """ + Set serial number register + """ + assert len(serial_number) > 0 + assert len(serial_number) <= 8 + serial_number_string = str(serial_number, 'ascii') + serial_number_int = int(serial_number_string, 16) + self.poke32(self.SERIAL_NO_LO_OFFSET, serial_number_int & 0xFFFFFFFF) + self.poke32(self.SERIAL_NO_HI_OFFSET, serial_number_int >> 32) + + def set_cmi_device_ready(self, ready=True): + """ + Inform CMI partner that this device is ready for PCI-Express communication. + """ + value = 1 if ready else 0 + self.poke32(self.CMI_OFFSET, value) + + def get_cmi_status(self): + """ + Return true if upstream CMI device was found. + """ + return bool(self.peek32(self.CMI_OFFSET)) diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_periphs.py b/mpm/python/usrp_mpm/periph_manager/x4xx_periphs.py new file mode 100644 index 000000000..be0487872 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_periphs.py @@ -0,0 +1,1445 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +X4xx peripherals +""" + +import re +import time +import signal +import struct +from multiprocessing import Process, Event +from statistics import mean +from usrp_mpm import lib # Pulls in everything from C++-land +from usrp_mpm.sys_utils import i2c_dev +from usrp_mpm.sys_utils.gpio import Gpio +from usrp_mpm.sys_utils.uio import UIO +from usrp_mpm.mpmutils import poll_with_timeout +from usrp_mpm.sys_utils.sysfs_thermal import read_thermal_sensor_value +from usrp_mpm.periph_manager.common import MboardRegsCommon + +class DioControl: + """ + DioControl acts as front end for DIO AUX BOARD + + The DioControl class uses three hardware resources to control the behavior + of the board, which are + * I2C extender + * MB registers + * MB cpld registers + + DioControl supports arbitrary methods of addressing the pins on the + frontend. The current implementation supports two ways of pin addressing: + HDMI and DIO. Use set_port_mapping to switch between both of them. + + When using HDMI as pin addressing scheme you have to give the real pin + number of the HDMI adapter like this:: + ┌───────────────────────────────┐ + └┐19 17 15 13 11 09 07 05 03 01┌┘ + └┐ 18 16 14 12 10 08 06 04 02┌┘ + └───────────────────────────┘ + Be aware that not all pins are accessible. The DioControl class will warn + about the usage of unassigned pins. + + The second option is the DIO addressing scheme. Here all user accessible + pins are numbered along the HDMI pin numbers which gives a pin table like + this:: + ┌───────────────────────────────┐ + └┐11 -- 09 08 -- 05 04 -- 01 00┌┘ + └┐ -- 10 -- 07 06 -- 03 02 --┌┘ + └───────────────────────────┘ + + Within the MPM shell one can query the state of the DIO board using + dio_status. This gives an output like this:: + HDMI mapping | PORT A | PORT B + --------------+-------------------------+------------------------- + voltage | OFF - PG:NO - EXT:OFF | 1V8 - PG:YES - EXT:OFF + --------------+-------------------------+------------------------- + master | 0.. 00.0 0.00 .00. 00.1 | 0.. 00.0 0.00 .00. 00.1 + direction | 0.. 00.0 0.00 .00. 00.1 | 0.. 00.0 0.00 .00. 00.0 + --------------+-------------------------+------------------------- + output | 0.. 00.0 0.00 .00. 00.1 | 0.. 00.0 0.00 .00. 00.0 + input | 0.. 00.0 0.00 .00. 00.1 | 0.. 00.0 0.00 .00. 00.0 + The table displays the current state of HDMI port A and B provided by the + DIO board as well as the state of the corresponding register maps and GPIO + pins in a user readable form. + + The header shows the active mapping and the port names. Change the + mapping with set_port_mapping. + + The first row shows the voltage state of each port. Voltage can be one of + the states in (OFF, 1V8, 2V5, 3V3). Change the power state by using + set_voltage_level. When the voltage level is set to OFF, the corresponding + GPIO pin EN_PORT is set low, high otherwise. + When voltage is set to one of the other states EN_PORT<x> is set to high and + EN_PORT<x>_<?V?> is set accordingly where 1V8 corresponds to 2V5 and 3V3 + being both low. PG shows whether the PG (power good) pin corresponding to + the port (PORT<x>_PG) is high. This is NO if power is OFF and YES otherwise. + EXT shows whether EN_EXT_PWR_<x> is enabled for the port. Change the + external power using set_external_power. + Note: A port must have a reasonable voltage level assigned to it before + changes to the output register takes effect on the HDMI port pins or + pin states of the HDMI port can be read in input register. + + The master row shows the pin assignments for the master register which + decides whether PS (1) or FPGA (0) drives output register pins. Change + values using set_master_pin(s). + + The direction row shows the pin assignments for the direction register which + decides whether the pin is written (1) or read (0) by the FPGA. Change + values using set_direction_pin(s). + + The output and input rows shows the pin assignments for the FPGAs input and + output registers. Only the output register pins can be changed. Change + values using set_output_pin(s). + + """ + + # Available DIO ports + DIO_PORTS = ("PORTA", "PORTB") + # Available voltage levels + DIO_VOLTAGE_LEVELS = ("OFF", "1V8", "2V5", "3V3") + + # For each mapping supported by DioControl the class needs the following + # information + # * map_name: name of the mapping (in uppercase) + # * pin_names: names of the pins starting with smallest. Unassignable PINS + # must be named as well + # * port_map: mapping table from FPGA register indices to pin indices. A + # mapping of (4, 2) means pin 4 is mapped to register bit 0 and + # pin2 is mapped to register bit 1. Only assignable pins + # may appear in the mapping. + # * first_pin: index of the first pin for the mapping + + # HDMI mapping constants + HDMI_MAP_NAME = "HDMI" + HDMI_PIN_NAMES = ("Data2+", "Data2_SHD", "Data2-", "Data1+", "Data1_SHD", + "Data1-", "Data0+", "Data0_SHD", "Data0-", "CLK+", + "CLK_SHD", "CLK-", "RESERVED", "HEC_Data-", "SCL", + "SDA", "HEC_GND", "V+", "HEC_Data+") + HDMI_PORT_MAP = {DIO_PORTS[0]: (3, 1, 4, 6, 9, 7, 10, 12, 15, 13, 16, 19), + DIO_PORTS[1]: (16, 19, 15, 13, 10, 12, 9, 7, 4, 6, 3, 1)} + HDMI_FIRST_PIN = 1 + + # DIO mapping constants + DIO_MAP_NAME = "DIO" + DIO_PIN_NAMES = ("DIO0", "DIO1", "DIO2", "DIO3", "DIO4", "DIO5", + "DIO6", "DIO7", "DIO8", "DIO9", "DIO10", "DIO11") + DIO_PORT_MAP = {DIO_PORTS[0]: (1, 0, 2, 3, 5, 4, 6, 7, 9, 8, 10, 11), + DIO_PORTS[1]: (10, 11, 9, 8, 6, 7, 5, 4, 2, 3, 1, 0)} + DIO_FIRST_PIN = 0 + + # Register layout/size constants + PORT_BIT_SIZE = 16 # number of bits used in register per port + PORT_USED_BITS_MASK = 0xFFF # masks out lower 12 of 16 bits used per port + + # DIO registers addresses in FPGA + FPGA_DIO_REGISTER_BASE = 0x2000 + FPGA_DIO_MASTER_REGISTER = FPGA_DIO_REGISTER_BASE + FPGA_DIO_DIRECTION_REGISTER = FPGA_DIO_REGISTER_BASE + 0x4 + FPGA_DIO_INPUT_REGISTER = FPGA_DIO_REGISTER_BASE + 0x8 + FPGA_DIO_OUTPUT_REGISTER = FPGA_DIO_REGISTER_BASE + 0xC + # DIO registers addresses in CPLD + CPLD_DIO_DIRECTION_REGISTER = 0x30 + + class _PortMapDescriptor: + """ + Helper class to hold port mapping relevant information + """ + def __init__(self, name, pin_names, map, first_pin): + self.name = name + self.pin_names = pin_names + self.map = map + self.first_pin = first_pin + + + class _PortControl: + """ + Helper class for controlling ports on the I2C expander + """ + def __init__(self, port): + assert port in DioControl.DIO_PORTS + prefix = "DIOAUX_%s" % port + + self.enable = Gpio('%s_ENABLE' % prefix, Gpio.OUTPUT) + self.en_3v3 = Gpio('%s_3V3' % prefix, Gpio.OUTPUT) + self.en_2v5 = Gpio('%s_2V5' % prefix, Gpio.OUTPUT) + self.ext_pwr = Gpio('%s_ENABLE_EXT_PWR' % prefix, Gpio.OUTPUT) + self.power_good = Gpio('%s_PWR_GOOD' % prefix, Gpio.INPUT) + + + def __init__(self, mboard_regs, mboard_cpld, log): + """ + Initializes access to hardware components as well as creating known + port mappings + :param log: logger to be used for output + """ + self.log = log.getChild(self.__class__.__name__) + self.port_control = {port: self._PortControl(port) for port in self.DIO_PORTS} + self.mboard_regs = mboard_regs + self.mboard_cpld = mboard_cpld + + # initialize port mapping for HDMI and DIO + self.port_mappings = {} + self.mapping = None + self.port_mappings[self.HDMI_MAP_NAME] = self._PortMapDescriptor( + self.HDMI_MAP_NAME, self.HDMI_PIN_NAMES, + self.HDMI_PORT_MAP, self.HDMI_FIRST_PIN) + self.port_mappings[self.DIO_MAP_NAME] = self._PortMapDescriptor( + self.DIO_MAP_NAME, self.DIO_PIN_NAMES, + self.DIO_PORT_MAP, self.DIO_FIRST_PIN) + self.set_port_mapping(self.HDMI_MAP_NAME) + self.log.trace("Spawning DIO fault monitors...") + self._tear_down_monitor = Event() + self._dio0_fault_monitor = Process( + target=self._monitor_dio_fault, + args=('A', "DIO_INT0", self._tear_down_monitor) + ) + self._dio1_fault_monitor = Process( + target=self._monitor_dio_fault, + args=('B', "DIO_INT1", self._tear_down_monitor) + ) + signal.signal(signal.SIGINT, self._monitor_int_handler) + self._dio0_fault_monitor.start() + self._dio1_fault_monitor.start() + + def _monitor_dio_fault(self, dio_port, fault, tear_down): + """ + Monitor the DIO_INT lines to detect an external power fault. + If there is a fault, turn off external power. + """ + self.log.trace("Launching monitor loop...") + fault_line = Gpio(fault, Gpio.FALLING_EDGE) + while True: + try: + if fault_line.event_wait(): + # If we saw a fault, disable the external power + self.log.warning("DIO fault occurred on port {} - turning off external power" + .format(dio_port)) + self.set_external_power(dio_port, 0) + # If the event wait gets interrupted because we are trying to tear down then stop + # the monitoring process. If not, keep monitoring + except InterruptedError: + pass + if tear_down.is_set(): + break + + def _monitor_int_handler(self, signum, frame): + """ + If we see an expected interrupt signal, mark the DIO fault monitors + for tear down. + """ + self._tear_down_monitor.set() + + # -------------------------------------------------------------------------- + # Helper methods + # -------------------------------------------------------------------------- + def _map_to_register_bit(self, port, pin): + """ + Maps a pin denoted in current mapping scheme to a corresponding bit in + the register map. + :param port: port to do the mapping on + :param pin: pin (in current mapping scheme) + :return: bit position in register map + :raises RuntimeError: pin is not in range of current mapping scheme + or not user assignable. + """ + assert isinstance(pin, int) + port = self._normalize_port_name(port) + first_pin = self.mapping.first_pin + last_pin = first_pin + len(self.mapping.pin_names) - 1 + port_map = self.mapping.map[port] + + if not (first_pin <= pin <= last_pin): + raise RuntimeError("Pin must be in range [%d,%d]. Given pin: %d" % + (first_pin, last_pin, pin)) + if pin not in port_map: + raise RuntimeError("Pin %d (%s) is not a user assignable pin." % + (pin, + self.mapping.pin_names[pin - first_pin])) + + # map pin back to register bit + bit = port_map.index(pin) + # lift register bit up by PORT_BIT_SIZE for port b + bit = bit if port == self.DIO_PORTS[0] else bit + self.PORT_BIT_SIZE + return bit + + def _calc_register_value(self, register, port, pin, value): + """ + Recalculates register value. + + Current register state is read and the bit that corresponds to the + values given by port and pin is determined. The register content is + changed at position of bit to what is given by value. + + Note: This routine only reads the current and calculates the new + register value. It is up to the callee to set the register value. + :param register: Address of the register value to recalculate + :param port: port associated with pin + :param pin: pin to change (will be mapped to bit according to + current mapping scheme an given port) + :param value: new bit value to set + :return: new register value. + """ + assert value in [0, 1] + + content = self.mboard_regs.peek32(register) + bit = self._map_to_register_bit(port, pin) + content = (content | 1 << bit) if value == 1 else (content & ~(1 << bit)) + return content + + def _set_pin_values(self, port, values, set_method): + """ + Helper method to assign multiple pins in one call. + :param port: Port to set pins on + :param values: New pin assignment represented by an integer. Each bit of + values corresponds to a pin on board according to current + mapping scheme. Bits that do not correspond to a pin in + the current mapping scheme are skipped. + :param set_method: method to be used to set/unset a pin. Signature of + set_method is (port, pin). + """ + first_pin = self.mapping.first_pin + port = self._normalize_port_name(port) + for i, pin_name in enumerate(self.mapping.pin_names): + if i + first_pin in self.mapping.map[port]: + set_method(port, i + first_pin, int(values & 1 << i != 0)) + + # -------------------------------------------------------------------------- + # Helper to convert abbreviations to constants defined in DioControl + # -------------------------------------------------------------------------- + + def _normalize_mapping(self, mapping): + """ + Map name to one of the key in self.port_mappings. + :param mapping: mapping name or any abbreviation by removing letters + from the end of the name + :return: Key found for mapping name + :raises RuntimeError: no matching mapping could be found + """ + assert isinstance(mapping, str) + mapping = mapping.upper() + mapping_names = self.port_mappings.keys() + try: + # search for abbr of mapping in mapping names + index = [re.match("^%s" % mapping, name) is not None for name in mapping_names].index(True) + return list(self.port_mappings.keys())[index] + except ValueError: + raise RuntimeError("Mapping %s not found in %s" % (mapping, mapping_names)) + + def _normalize_port_name(self, name): + """ + Map port name to the normalized form of self.DIO_PORTS + :param name: port name or abbreviation with A or B, case insensitive + :return: normalized port name + :raises RuntimeError: name could not be normalized + """ + assert isinstance(name, str) + if not name.upper() in self.DIO_PORTS + ("A", "B"): + raise RuntimeError("Could not map %s to port name" % name) + return self.DIO_PORTS[0] if name.upper() in (self.DIO_PORTS[0], "A") \ + else self.DIO_PORTS[1] + + # -------------------------------------------------------------------------- + # Helper to format status output + # -------------------------------------------------------------------------- + + def _get_port_voltage(self, port): + """ + Format voltage table cell value. + """ + port_control = self.port_control[port] + result = "" + if port_control.enable.get() == 0: + result += self.DIO_VOLTAGE_LEVELS[0] + elif port_control.en_2v5.get() == 1: + result += self.DIO_VOLTAGE_LEVELS[2] + elif port_control.en_3v3.get() == 1: + result += self.DIO_VOLTAGE_LEVELS[3] + else: + result += self.DIO_VOLTAGE_LEVELS[1] + result += " - PG:" + result += "YES" if port_control.power_good.get() else "NO" + result += " - EXT:" + result += "ON" if port_control.ext_pwr.get() else "OFF" + return result + + def _get_voltage(self): + """ + Format voltage table cells + """ + return [self._get_port_voltage(port) for port in self.DIO_PORTS] + + def _format_register(self, port, content): + """ + Format a port value according to current mapping scheme. Pins are + grouped by 4. Pins which are not user assignable are marked with a dot. + :param content: register content + :return: register content as pin assignment according to current + mapping scheme + """ + result = "" + first_pin = self.mapping.first_pin + pin_names = self.mapping.pin_names + mapping = self.mapping.map[port] + for i, _ in enumerate(pin_names): + if i % 4 == 0 and i > 0: + result = " " + result + if i + first_pin in mapping: + result = str(int(content & (1 << mapping.index(i + first_pin)) != 0)) + result + else: + result = "." + result + return result + + def _format_registers(self, content): + """ + Formats register content for port A and B + :param content: + :return: + """ + port_a = content & self.PORT_USED_BITS_MASK + port_b = (content >> self.PORT_BIT_SIZE) & self.PORT_USED_BITS_MASK + return [self._format_register(self.DIO_PORTS[0], port_a), + self._format_register(self.DIO_PORTS[1], port_b)] + + def _format_row(self, values, fill=" ", delim="|"): + """ + Format a table row with fix colums widths. Generates row spaces using + value list with empty strings and "-" as fill and "+" as delim. + :param values: cell values (list of three elements) + :param fill: fill character to use (space by default) + :param delim: delimiter character between columns + :return: formated row + """ + col_widths = [14, 25, 25] + return delim.join([ + fill + values[i].ljust(width - len(fill), fill) + for i, width in enumerate(col_widths) + ]) + "\n" + + # -------------------------------------------------------------------------- + # Public API + # -------------------------------------------------------------------------- + + def tear_down(self): + """ + Mark the DIO monitoring processes for tear down and terminate the processes + """ + self._tear_down_monitor.set() + self._dio0_fault_monitor.terminate() + self._dio1_fault_monitor.terminate() + self._dio0_fault_monitor.join(3) + self._dio1_fault_monitor.join(3) + if self._dio0_fault_monitor.is_alive() or \ + self._dio1_fault_monitor.is_alive(): + self.log.warning("DIO monitor didn't exit properly") + + def set_port_mapping(self, mapping): + """ + Change the port mapping to mapping. Mapping must denote a mapping found + in this.port_mappings.keys() or any abbreviation allowed by + _normalize_port_mapping. The mapping does not change the status of the + FPGA registers. It only changes the status display and the way calls + to set_pin_<register_name>(s) are interpreted. + :param mapping: new mapping to be used + :raises RuntimeError: mapping could not be found + """ + assert isinstance(mapping, str) + map_name = self._normalize_mapping(mapping) + if not map_name in self.port_mappings.keys(): + raise RuntimeError("Could not map %s to port mapping" % mapping) + self.mapping = self.port_mappings[map_name] + + def set_pin_master(self, port, pin, value=1): + """ + Set master pin of a port. The master pin decides whether the DIO board + pin is driven by the PS (1) or FPGA (0) register interface. To change + the pin value the current register content is read first and modified + before it is written back, so the register must be readable. + :param port: port to change master assignment on + :param pin: pin to change + :param value: desired pin value + """ + content = self._calc_register_value(self.FPGA_DIO_MASTER_REGISTER, + port, pin, value) + self.mboard_regs.poke32(self.FPGA_DIO_MASTER_REGISTER, content) + + def set_pin_masters(self, port, values): + """ + Set all master pins of a port at once using a bit mask. + :param port: port to change master pin assignment + :param values: New pin assignment represented by an integer. Each bit of + values corresponds to a pin on board according to current + mapping scheme. Bits that do not correspond to a pin in + the current mapping scheme are skipped. + """ + self._set_pin_values(port, values, self.set_pin_master) + + def set_pin_direction(self, port, pin, value=1): + """ + Set direction pin of a port. The direction pin decides whether the DIO + external pin is used as an output (write - value is 1) or input (read - + value is 0). To change the pin value the current register content is + read first and modified before it is written back, so the register must + be readable. + Besides the FPGA register map, the CPLD register map is also written. To + prevent the internal line to be driven by FGPA and DIO board at the same + time the CPLD register is written first if the direction will become an + output. If direction will become an input the FPGA register is written + first. + :param port: port to change direction assignment on + :param pin: pin to change + :param value: desired pin value + """ + content = self._calc_register_value(self.FPGA_DIO_DIRECTION_REGISTER, + port, pin, value) + # When setting direction pin, order matters. Always switch the component + # first that will get the driver disabled. + # This ensures that there wont be two drivers active at a time. + if value == 1: # FPGA is driver => write DIO register first + self.mboard_cpld.poke32(self.CPLD_DIO_DIRECTION_REGISTER, content) + self.mboard_regs.poke32(self.FPGA_DIO_DIRECTION_REGISTER, content) + else: # DIO is driver => write FPGA register first + self.mboard_regs.poke32(self.FPGA_DIO_DIRECTION_REGISTER, content) + self.mboard_cpld.poke32(self.CPLD_DIO_DIRECTION_REGISTER, content) + # Read back values to ensure registers are in sync + cpld_content = self.mboard_cpld.peek32(self.CPLD_DIO_DIRECTION_REGISTER) + mbrd_content = self.mboard_regs.peek32(self.FPGA_DIO_DIRECTION_REGISTER) + if not ((cpld_content == content) and (mbrd_content == content)): + raise RuntimeError("Direction register content mismatch. Expected:" + "0x%0.8X, CPLD: 0x%0.8X, FPGA: 0x%0.8X." % + (content, cpld_content, mbrd_content)) + + def set_pin_directions(self, port, values): + """ + Set all direction pins of a port at once using a bit mask. + :param port: port to change direction pin assignment + :param values: New pin assignment represented by an integer. Each bit of + values corresponds to a pin on board according to current + mapping scheme. Bits that do not correspond to a pin in + the current mapping scheme are skipped. + """ + self._set_pin_values(port, values, self.set_pin_direction) + + def set_pin_output(self, port, pin, value=1): + """ + Set output value of a pin on a port. Setting this value only takes + effect if the direction of the corresponding pin of this port is set + accordingly. To change the pin value the current register content is + read first and modified before it is written back, so the register must + be readable. + :param port: port to change output assignment on + :param pin: pin to change + :param value: desired pin value + """ + content = self._calc_register_value(self.FPGA_DIO_OUTPUT_REGISTER, + port, pin, value) + self.mboard_regs.poke32(self.FPGA_DIO_OUTPUT_REGISTER, content) + + def set_pin_outputs(self, port, values): + """ + Set all output pins of a port at once using a bit mask. + :param port: port to change direction pin assignment + :param values: New pin assignment represented by an integer. Each bit of + values corresponds to a pin on board according to current + mapping scheme. Bits that do not correspond to a pin in + the current mapping scheme are skipped. + """ + self._set_pin_values(port, values, self.set_pin_output) + + def get_pin_input(self, port, pin): + """ + Returns the input pin value of a port. + If the pin is not assignable in the current mapping None is returned. + + :param port: port to read pin value from + :param pin: pin value to read + :returns: actual pin value or None if pin is not assignable + """ + port = self._normalize_port_name(port) + + register = self.mboard_regs.peek32(self.FPGA_DIO_INPUT_REGISTER) + if port == self.DIO_PORTS[1]: + register = register >> self.PORT_BIT_SIZE + register &= self.PORT_USED_BITS_MASK + + mapping = self.mapping.map[port] + if not pin in mapping: + raise RuntimeError("Pin %d (%s) is not a user readable pin." % + (pin, + self.mapping.pin_names[pin - self.mapping.first_pin])) + return 0 if (register & (1 << mapping.index(pin)) == 0) else 1 + + def get_pin_inputs(self, port): + """ + Returns a bit mask of all pins for the given port. + + :param port: port to read input pins from + :returns: Bit map of input pins, each bit of pins corresponds to a pin + on board according to current mapping scheme. Unused pins + stay zero + """ + result = 0 + first_pin = self.mapping.first_pin + pin_names = self.mapping.pin_names + port = self._normalize_port_name(port) + mapping = self.mapping.map[port] + for i, name in enumerate(pin_names): + if i + first_pin in mapping: + if self.get_pin_input(port, i + first_pin): + result |= 1 << i + return result + + def set_voltage_level(self, port, level): + """ + Change voltage level of a port. This is how EN_<port>, EN_<port>_2V5 and + EN_<port>_3V3 are set according to level:: + level EN_<port> EN_<port>_2V5 EN_<port>_3V3 + off 0 0 0 + 1V8 1 0 0 + 2V5 1 1 0 + 3V3 1 0 1 + If level is set to anything other than off this method waits for + <port>_PG to go high. Waiting stops as soon as <port>_PG goes high or + a timeout of 1s occurs. + Note: All pins are set to zero first before the new level is applied. + :param port: port to change power level for + :param level: new power level + :raises RuntimeError: power good pin did not go high + """ + port = self._normalize_port_name(port) + level = level.upper() + assert port in self.DIO_PORTS + assert level in self.DIO_VOLTAGE_LEVELS + port_control = self.port_control[port] + + port_control.enable.set(0) + port_control.en_2v5.set(0) + port_control.en_3v3.set(0) + if level == self.DIO_VOLTAGE_LEVELS[2]: + port_control.en_2v5.set(1) + elif level == self.DIO_VOLTAGE_LEVELS[3]: + port_control.en_3v3.set(1) + + # wait for <port>_PG to go high + if not level == self.DIO_VOLTAGE_LEVELS[0]: # off + port_control.enable.set(1) + if not poll_with_timeout( + lambda: port_control.power_good.get() == 1, 1000, 10): + raise RuntimeError( + "Power good pin did not go high after power up") + + def set_external_power(self, port, value): + """ + Change EN_EXT_PWR_<port> to value. + :param port: port to change external power level for + :param value: 1 to enable external power, 0 to disable + :raise RuntimeError: port or pin value could not be mapped + """ + port = self._normalize_port_name(port) + value = int(value) + assert value in (0, 1) + assert port in self.DIO_PORTS + self.port_control[port].ext_pwr.set(value) + + def status(self): + """ + Build a full status string for the DIO AUX board, including + I2C pin states and register content in a human readable form. + :return: board status + """ + result = "\n" \ + + self._format_row(["%s mapping" % self.mapping.name, self.DIO_PORTS[0], self.DIO_PORTS[1]]) \ + + self._format_row(["", "", ""], "-", "+") \ + + self._format_row(["voltage"] + self._get_voltage()) \ + + self._format_row(["", "", ""], "-", "+") + + register = self.mboard_regs.peek32(self.FPGA_DIO_MASTER_REGISTER) + result += self._format_row(["master"] + self._format_registers(register)) + + register = self.mboard_regs.peek32(self.FPGA_DIO_DIRECTION_REGISTER) + result += self._format_row(["direction"] + self._format_registers(register)) + + result += self._format_row(["", "", ""], "-", "+") + + register = self.mboard_regs.peek32(self.FPGA_DIO_OUTPUT_REGISTER) + result += self._format_row(["output"] + self._format_registers(register)) + + register = self.mboard_regs.peek32(self.FPGA_DIO_INPUT_REGISTER) + result += self._format_row(["input"] + self._format_registers(register)) + return result + + def debug(self): + """ + Create a debug string containing the FPGA register maps. The CPLD + direction register is not part of the string as the DioControl maintains + it in sync with the FPGA direction register. + :return: register states for debug purpose in human readable form. + """ + master = format(self.mboard_regs.peek32(self.FPGA_DIO_MASTER_REGISTER), "032b") + direction = format(self.mboard_regs.peek32(self.FPGA_DIO_DIRECTION_REGISTER), "032b") + output = format(self.mboard_regs.peek32(self.FPGA_DIO_OUTPUT_REGISTER), "032b") + input = format(self.mboard_regs.peek32(self.FPGA_DIO_INPUT_REGISTER), "032b") + return "\nmaster: " + " ".join(re.findall('....', master)) + "\n" + \ + "direction: " + " ".join(re.findall('....', direction)) + "\n" + \ + "output: " + " ".join(re.findall('....', output)) + "\n" + \ + "input: " + " ".join(re.findall('....', input)) + + +class MboardRegsControl(MboardRegsCommon): + """ + Control the FPGA Motherboard registers + + A note on using this object: All HDL-specifics should be encoded in this + class. That means that calling peek32 or poke32 on this class should only be + used in rare exceptions. The call site shouldn't have to know about + implementation details behind those registers. + + To do multiple peeks/pokes without opening and closing UIO objects, it is + possible to do that from outside the object: + >>> with mb_regs_control.regs: + ... mb_regs_control.poke32(addr0, data0) + ... mb_regs_control.poke32(addr1, data1) + """ + # Motherboard registers + # pylint: disable=bad-whitespace + # 0 through 0x14 are common regs (see MboardRegsCommon) + MB_CLOCK_CTRL = 0x0018 + MB_PPS_CTRL = 0x001C + MB_BUS_CLK_RATE = 0x0020 + MB_BUS_COUNTER = 0x0024 + MB_GPIO_CTRL = 0x002C + MB_GPIO_MASTER = 0x0030 + MB_GPIO_RADIO_SRC = 0x0034 + # MB_NUM_TIMEKEEPERS = 0x0048 Shared with MboardRegsCommon + MB_SERIAL_NO_LO = 0x004C + MB_SERIAL_NO_HI = 0x0050 + MB_MFG_TEST_CTRL = 0x0054 + MB_MFG_TEST_STATUS = 0x0058 + # QSFP port info consists of 2 ports of 4 lanes each, + # both separated by their corresponding stride value + MB_QSFP_PORT_INFO = 0x0060 + MB_QSFP_LANE_STRIDE = 0x4 + MB_QSFP_PORT_STRIDE = 0x10 + # Versioning registers + MB_VER_FPGA = 0x0C00 + MB_VER_CPLD_IFC = 0x0C10 + MB_VER_RF_CORE_DB0 = 0x0C20 + MB_VER_RF_CORE_DB1 = 0x0C30 + MB_VER_GPIO_IFC_DB0 = 0x0C40 + MB_VER_GPIO_IFC_DB1 = 0x0C50 + CURRENT_VERSION_OFFSET = 0x0 + OLDEST_COMPATIBLE_VERSION_OFFSET = 0x4 + VERSION_LAST_MODIFIED_OFFSET = 0x8 + # Timekeeper registers start at 0x1000 (see MboardRegsCommon) + + # Clock control register bit masks + CLOCK_CTRL_PLL_SYNC_DELAY = 0x00FF0000 + CLOCK_CTRL_PLL_SYNC_DONE = 0x00000200 + CLOCK_CTRL_PLL_SYNC_TRIGGER = 0x00000100 + CLOCK_CTRL_TRIGGER_IO_SEL = 0x00000030 + CLOCK_CTRL_TRIGGER_PPS_SEL = 0x00000003 + + MFG_TEST_CTRL_GTY_RCV_CLK_EN = 0x00000001 + MFG_TEST_CTRL_FABRIC_CLK_EN = 0x00000002 + MFG_TEST_AUX_REF_FREQ = 0x03FFFFFF + # Clock control register values + CLOCK_CTRL_TRIG_IO_INPUT = 0 + CLOCK_CTRL_PPS_INT_25MHz = 0 + CLOCK_CTRL_TRIG_IO_PPS_OUTPUT = 0x10 + CLOCK_CTRL_PPS_INT_10MHz = 0x1 + CLOCK_CTRL_PPS_EXT = 0x2 + # pylint: enable=bad-whitespace + + def __init__(self, label, log): + MboardRegsCommon.__init__(self, label, log) + def peek32(address): + """ + Safe peek (opens and closes UIO). + """ + with self.regs: + return self.regs.peek32(address) + def poke32(address, value): + """ + Safe poke (opens and closes UIO). + """ + with self.regs: + self.regs.poke32(address, value) + # MboardRegsCommon.poke32() and ...peek32() don't open the UIO, so we + # overwrite them with "safe" versions that do open the UIO. + self.peek32 = peek32 + self.poke32 = poke32 + + def set_serial_number(self, serial_number): + """ + Set serial number register + """ + assert len(serial_number) > 0 + assert len(serial_number) <= 8 + serial_number = serial_number + b'\x00' * (8 - len(serial_number)) + (sn_lo, sn_hi) = struct.unpack("II", serial_number) + with self.regs: + self.poke32(self.MB_SERIAL_NO_LO, sn_lo) + self.poke32(self.MB_SERIAL_NO_HI, sn_hi) + + def get_compat_number(self): + """get FPGA compat number + + This function reads back FPGA compat number. + The return is a tuple of + 2 numbers: (major compat number, minor compat number) + X4xx stores this tuple in MB_VER_FPGA instead of MB_COMPAT_NUM + """ + version = self.peek32(self.MB_VER_FPGA + self.CURRENT_VERSION_OFFSET) + major = (version >> 23) & 0x1FF + minor = (version >> 12) & 0x7FF + return (major, minor) + + def get_db_gpio_ifc_version(self, slot_id): + """ + Get the version of the DB GPIO interface for the corresponding slot + """ + if slot_id == 0: + current_version = self.peek32(self.MB_VER_GPIO_IFC_DB0) + elif slot_id == 1: + current_version = self.peek32(self.MB_VER_GPIO_IFC_DB1) + else: + raise RuntimeError("Invalid daughterboard slot id: {}".format(slot_id)) + + major = (current_version>>23) & 0x1ff + minor = (current_version>>12) & 0x7ff + build = current_version & 0xfff + return (major, minor, build) + + def _get_qsfp_lane_value(self, port, lane): + addr = self.MB_QSFP_PORT_INFO + (port * self.MB_QSFP_PORT_STRIDE) \ + + (lane * self.MB_QSFP_LANE_STRIDE) + return (self.peek32(addr) >> 8) & 0xFF + + def _get_qsfp_type(self, port=0): + """ + Read the type of qsfp port is in the specified port + """ + x4xx_qsfp_types = { + 0: "", # Port not connected + 1: "1G", + 2: "10G", + 3: "A", # Aurora + 4: "W", # White Rabbit + 5: "100G" + } + + lane_0_val = self._get_qsfp_lane_value(port, 0) + num_lanes = 1 + + # Because we have qsfp, we could have up to 4x connection at the port + if lane_0_val > 0: + for lane in range(1, 4): + lane_val = self._get_qsfp_lane_value(port, lane) + if lane_val == lane_0_val: + num_lanes += 1 + + if num_lanes > 1: + return str(num_lanes) + "x" + x4xx_qsfp_types.get(lane_0_val, "") + + return x4xx_qsfp_types.get(lane_0_val, "") + + def get_fpga_type(self): + """ + Reads the type of the FPGA image currently loaded + Returns a string with the type (ie CG, XG, C2, etc.) + """ + x4xx_fpga_types_by_qsfp = { + ("", ""): "", + ("10G", "10G"): "XG", + ("10G", ""): "X1", + ("2x10G", ""): "X2", + ("4x10G", ""): "X4", + ("4x10G", "100G"): "X4C", + ("100G", "100G"): "CG", + ("100G", ""): "C1" + } + + qsfp0_type = self._get_qsfp_type(0) + qsfp1_type = self._get_qsfp_type(1) + self.log.trace("QSFP types: ({}, {})".format(qsfp0_type, qsfp1_type)) + try: + fpga_type = x4xx_fpga_types_by_qsfp[(qsfp0_type, qsfp1_type)] + except KeyError: + self.log.warning("Unrecognized QSFP type combination: ({}, {})" + .format(qsfp0_type, qsfp1_type)) + fpga_type = "" + + if not fpga_type and self.is_pcie_present(): + fpga_type = "LV" + + return fpga_type + + def is_pcie_present(self): + """ + Return True in case the PCI_EXPRESS_BIT is set in the FPGA image, which + means there is a PCI-Express core. False otherwise. + """ + regs_val = self.peek32(self.MB_DEVICE_ID) + return (regs_val & 0x80000000) != 0 + + def is_pll_sync_done(self): + """ + Return state of PLL sync bit from motherboard clock control register. + """ + return bool(self.peek32(MboardRegsControl.MB_CLOCK_CTRL) & \ + self.CLOCK_CTRL_PLL_SYNC_DONE) + + def pll_sync_trigger(self, clock_ctrl_pps_src): + """ + Callback for LMK04832 driver to actually trigger the sync. Set PPS + source accordingly. + """ + with self.regs: + # Update clock control config register to use the currently relevant + # PPS source + config = self.peek32(self.MB_CLOCK_CTRL) + trigger_config = \ + (config & ~MboardRegsControl.CLOCK_CTRL_TRIGGER_PPS_SEL) \ + | clock_ctrl_pps_src + # trigger sync with appropriate configuration + self.poke32( + self.MB_CLOCK_CTRL, + trigger_config | self.CLOCK_CTRL_PLL_SYNC_TRIGGER) + # wait for sync done indication from FPGA + # The following value is in ms, it was experimentally picked. + pll_sync_timeout = 1500 # ms + result = poll_with_timeout(self.is_pll_sync_done, pll_sync_timeout, 10) + # de-assert sync trigger signal + self.poke32(self.MB_CLOCK_CTRL, trigger_config) + if not result: + self.log.error("PLL_SYNC_DONE not received within timeout") + return result + + def set_trig_io_output(self, enable_output): + """ + Enable TRIG I/O output. If enable_output is "True", then we set TRIG I/O + to be an output. If enable_output is "False", then we make it an input. + + Note that the clocking board also is involved in configuring the TRIG I/O. + This is only the part that configures the FPGA registers. + """ + with self.regs: + # prepare clock control FPGA register content + clock_ctrl_reg = self.peek32(MboardRegsControl.MB_CLOCK_CTRL) + clock_ctrl_reg &= ~self.CLOCK_CTRL_TRIGGER_IO_SEL + if enable_output: + clock_ctrl_reg |= self.CLOCK_CTRL_TRIG_IO_PPS_OUTPUT + else: + # for both input and off ensure FPGA does not drive trigger IO line + clock_ctrl_reg |= self.CLOCK_CTRL_TRIG_IO_INPUT + self.poke32(MboardRegsControl.MB_CLOCK_CTRL, clock_ctrl_reg) + + def configure_pps_forwarding(self, enable, master_clock_rate, prc_rate, delay): + """ + Configures the PPS forwarding to the sample clock domain (master + clock rate). This function assumes _sync_spll_clocks function has + already been executed. + + :param enable: Boolean to choose whether PPS is forwarded to the + sample clock domain. + + :param master_clock_rate: Master clock rate in MHz + + :param prc_rate: PRC rate in MHz + + :param delay: Delay in seconds from the PPS rising edge to the edge + occurence in the application. This value has to be in + range 0 < x <= 1. In order to forward the PPS signal + from base reference clock to sample clock an aligned + rising edge of the clock is required. This can be + created by the _sync_spll_clocks function. Based on the + greatest common divisor of the two clock rates there + are multiple occurences of an aligned edge each second. + One of these aligned edges has to be chosen for the + PPS forwarding by setting this parameter. + + :return: None, Exception on error + """ + # delay range check 0 < x <= 1 + if (delay <= 0 or delay > 1): + raise RuntimeError("The delay has to be in range 0 < x <= 1") + + with self.regs: + # configure delay in BRC clock domain + value = self.peek32(self.MB_CLOCK_CTRL) + pll_sync_delay = (value >> 16) & 0xFF + # pps_brc_delay constants required by HDL implementation + pps_brc_delay = pll_sync_delay + 2 - 1 + value = (value & 0x00FFFFFF) | (pps_brc_delay << 24) + self.poke32(self.MB_CLOCK_CTRL, value) + + # configure delay in PRC clock domain + # reduction by 4 required by HDL implementation + pps_prc_delay = (int(delay * prc_rate) - 4) & 0x3FFFFFF + if pps_prc_delay == 0: + # limitation from HDL implementation + raise RuntimeError("The calculated delay has to be greater than 0") + value = pps_prc_delay + + # configure clock divider + # reduction by 2 required by HDL implementation + prc_rc_divider = (int(master_clock_rate/prc_rate) - 2) & 0x3 + value = value | (prc_rc_divider << 28) + + # write configuration to PPS control register (with PPS disabled) + self.poke32(self.MB_PPS_CTRL, value) + + # enables PPS depending on parameter + if enable: + # wait for 1 second to let configuration settle for any old PPS pulse + time.sleep(1) + # update value with enabled PPS + value = value | (1 << 31) + # write final configuration to PPS control register + self.poke32(self.MB_PPS_CTRL, value) + return True + + def enable_ecpri_clocks(self, enable, clock): + """ + Enable or disable the export of FABRIC and GTY_RCV eCPRI + clocks. Main use case until we support eCPRI is manufacturing + testing. + """ + valid_clocks_list = ['gty_rcv', 'fabric', 'both'] + assert clock in valid_clocks_list + clock_enable_sig = 0 + if clock == 'gty_rcv': + clock_enable_sig = self.MFG_TEST_CTRL_GTY_RCV_CLK_EN + elif clock == 'fabric': + clock_enable_sig = self.MFG_TEST_CTRL_FABRIC_CLK_EN + else:# 'both' case + clock_enable_sig = (self.MFG_TEST_CTRL_GTY_RCV_CLK_EN | + self.MFG_TEST_CTRL_FABRIC_CLK_EN) + with self.regs: + clock_ctrl_reg = self.peek32(MboardRegsControl.MB_MFG_TEST_CTRL) + if enable: + clock_ctrl_reg |= clock_enable_sig + else: + clock_ctrl_reg &= ~clock_enable_sig + self.poke32(MboardRegsControl.MB_MFG_TEST_CTRL, clock_ctrl_reg) + + def get_fpga_aux_ref_freq(self): + """ + Return the tick count of an FPGA counter which measures the width of + the PPS signal on the FPGA_AUX_REF FPGA input using a 40 MHz clock. + Main use case until we support eCPRI is manufacturing testing. + A return value of 0 indicates absence of a valid PPS signal on the + FPGA_AUX_REF line. + """ + status_reg = self.peek32(self.MB_MFG_TEST_STATUS) + return status_reg & self.MFG_TEST_AUX_REF_FREQ + + +class CtrlportRegs: + """ + Control the FPGA Ctrlport registers + """ + # pylint: disable=bad-whitespace + IPASS_OFFSET = 0x000010 + MB_PL_SPI_CONFIG = 0x000020 + DB_SPI_CONFIG = 0x000024 + MB_PL_CPLD = 0x008000 + DB_0_CPLD = 0x010000 + DB_1_CPLD = 0x018000 + # pylint: enable=bad-whitespace + + min_mb_cpld_spi_divider = 2 + min_db_cpld_spi_divider = 5 + class MbPlCpldIface: + """ Exposes access to register mapped MB PL CPLD register space """ + SIGNATURE_OFFSET = 0x0000 + REVISION_OFFSET = 0x0004 + + SIGNATURE = 0x3FDC5C47 + MIN_REQ_REVISION = 0x20082009 + + def __init__(self, regs_iface, offset, log): + self.log = log + self.offset = offset + self.regs = regs_iface + + def peek32(self, addr): + return self.regs.peek32(addr + self.offset) + + def poke32(self, addr, val): + self.regs.poke32(addr + self.offset, val) + + def check_signature(self): + read_signature = self.peek32(self.SIGNATURE_OFFSET) + if self.SIGNATURE != read_signature: + self.log.error('MB PL CPLD signature {:X} does not match ' + 'expected value {:X}'.format(read_signature, self.SIGNATURE)) + raise RuntimeError('MB PL CPLD signature {:X} does not match ' + 'expected value {:X}'.format(read_signature, self.SIGNATURE)) + + def check_revision(self): + read_revision = self.peek32(self.REVISION_OFFSET) + if read_revision < self.MIN_REQ_REVISION: + error_message = ('MB PL CPLD revision {:X} is out of date. ' + 'Expected value {:X}. Update your CPLD image.' + .format(read_revision, self.MIN_REQ_REVISION)) + self.log.error(error_message) + raise RuntimeError(error_message) + + class DbCpldIface: + """ Exposes access to register mapped DB CPLD register spaces """ + def __init__(self, regs_iface, offset): + self.offset = offset + self.regs = regs_iface + + def peek32(self, addr): + return self.regs.peek32(addr + self.offset) + + def poke32(self, addr, val): + self.regs.poke32(addr + self.offset, val) + + def __init__(self, label, log): + self.log = log.getChild("CtrlportRegs") + self._regs_uio_opened = False + try: + self.regs = UIO( + label=label, + read_only=False + ) + except RuntimeError: + self.log.warning('Ctrlport regs could not be found. ' \ + 'MPM Endpoint to the FPGA is not part of this image.') + self.regs = None + # Initialize SPI interface to MB PL CPLD and DB CPLDs + self.set_mb_pl_cpld_divider(self.min_mb_cpld_spi_divider) + self.set_db_divider_value(self.min_db_cpld_spi_divider) + self.mb_pl_cpld_regs = self.MbPlCpldIface(self, self.MB_PL_CPLD, self.log) + self.mb_pl_cpld_regs.check_signature() + self.mb_pl_cpld_regs.check_revision() + self.db_0_regs = self.DbCpldIface(self, self.DB_0_CPLD) + self.db_1_regs = self.DbCpldIface(self, self.DB_1_CPLD) + + def init(self): + if not self._regs_uio_opened: + self.regs._open() + self._regs_uio_opened = True + + def deinit(self): + if self._regs_uio_opened: + self.regs._close() + self._regs_uio_opened = False + + def peek32(self, addr): + if self.regs is None: + raise RuntimeError('The ctrlport registers were never configured!') + if self._regs_uio_opened: + return self.regs.peek32(addr) + else: + with self.regs: + return self.regs.peek32(addr) + + def poke32(self, addr, val): + if self.regs is None: + raise RuntimeError('The ctrlport registers were never configured!') + if self._regs_uio_opened: + return self.regs.poke32(addr, val) + else: + with self.regs: + return self.regs.poke32(addr, val) + + def set_mb_pl_cpld_divider(self, divider_value): + if not self.min_mb_cpld_spi_divider <= divider_value <= 0xFFFF: + self.log.error('Cannot set MB CPLD SPI divider to invalid value {}' + .format(divider_value)) + raise RuntimeError('Cannot set MB CPLD SPI divider to invalid value {}' + .format(divider_value)) + self.poke32(self.MB_PL_SPI_CONFIG, divider_value) + + def set_db_divider_value(self, divider_value): + if not self.min_db_cpld_spi_divider <= divider_value <= 0xFFFF: + self.log.error('Cannot set DB SPI divider to invalid value {}' + .format(divider_value)) + raise RuntimeError('Cannot set DB SPI divider to invalid value {}' + .format(divider_value)) + self.poke32(self.DB_SPI_CONFIG, divider_value) + + def get_db_cpld_iface(self, db_id): + return self.db_0_regs if db_id == 0 else self.db_1_regs + + def get_mb_pl_cpld_iface(self): + return self.mb_pl_cpld_regs + + def enable_cable_present_forwarding(self, enable=True): + value = 1 if enable else 0 + self.poke32(self.IPASS_OFFSET, value) + + +# QSFP Adapter IDs according to SFF-8436 rev 4.9 table 30 +QSFP_IDENTIFIERS = { + 0x00: "Unknown or unspecified", + 0x01: "GBIC", + 0x02: "Module/connector soldered to motherboard (using SFF-8472)", + 0x03: "SFP/SFP+/SFP28", + 0x04: "300 pin XBI", + 0x05: "XENPAK", + 0x06: "XFP", + 0x07: "XFF", + 0x08: "XFP-E", + 0x09: "XPAK", + 0x0A: "X2", + 0x0B: "DWDM-SFP/SFP+ (not using SFF-8472)", + 0x0C: "QSFP (INF-8438)", + 0x0D: "QSFP+ or later (SFF-8436, SFF-8635, SFF-8665, SFF-8685 et al)", + 0x0E: "CXP or later", + 0x0F: "Shielded Mini Multilane HD0x4X", + 0x10: "Shielded Mini Multilane HD0x8X", + 0x11: "QSFP28 or later (SFF-8665 et al)", + 0x12: "CXP2 (aka CXP28) or later", + 0x13: "CDFP (Style0x1/Style2)", + 0x14: "Shielded Mini Multilane HD0x4X Fanout Cable", + 0x15: "Shielded Mini Multilane HD0x8X Fanout Cable", + 0x16: "CDFP (Style0x3)" +} + +# QSFP revison compliance according to SFF-8636 rev 2.9 table 6-3 +QSFP_REVISION_COMPLIANCE = { + 0x00: "Not specified.", + 0x01: "SFF-8436 Rev 4.8 or earlier", + 0x02: "SFF-8436 Rev 4.8 or earlier (except 0x186-0x189)", + 0x03: "SFF-8636 Rev 1.3 or earlier", + 0x04: "SFF-8636 Rev 1.4", + 0x05: "SFF-8636 Rev 1.5", + 0x06: "SFF-8636 Rev 2.0", + 0x07: "SFF-8636 Rev 2.5, 2.6 and 2.7", + 0x08: "SFF-8636 Rev 2.8 or later" +} + +# QSFP connector types according to SFF-8029 rev 3.2 table 4-3 +QSFP_CONNECTOR_TYPE = { + 0x00: "Unknown or unspecified", + 0x01: "SC (Subscriber Connector)", + 0x02: "Fibre Channel Style 1 copper connector", + 0x03: "Fibre Channel Style 2 copper connector", + 0x04: "BNC/TNC (Bayonet/Threaded Neill-Concelman)", + 0x05: "Fibre Channel coax headers", + 0x06: "Fiber Jack", + 0x07: "LC (Lucent Connector)", + 0x08: "MT-RJ (Mechanical Transfer - Registered Jack)", + 0x09: "MU (Multiple Optical)", + 0x0A: "SG", + 0x0B: "Optical Pigtail", + 0x0C: "MPO 1x12 (Multifiber Parallel Optic)", + 0x0D: "MPO 2x16", + 0x20: "HSSDC II (High S peed Serial Data Connector)", + 0x21: "Copper pigtail", + 0x22: "RJ45 (Registered Jack)", + 0x23: "No separable connector", + 0x24: "MXC 2x16" +} + +class QSFPModule: + """ + QSFPModule enables access to the I2C register interface of an QSFP module. + + The class queries the module register using I2C commands according to + SFF-8486 rev 4.9 specification. + """ + + def __init__(self, gpio_modprs, gpio_modsel, devsymbol, log): + """ + modprs: Name of the GPIO pin that reports module presence + modsel: Name of the GPIO pin that controls ModSel of QSFP module + devsymbol: Symbol name of the device used for I2C communication + """ + + self.log = log.getChild('QSFP') + + # Hold the ModSelL GPIO low for communication over I2C. Because X4xx + # uses a I2C switch to communicate with the QSFP modules we can keep + # ModSelL low all the way long, because each QSFP module has + # its own I2C address (see SFF-8486 rev 4.9, chapter 4.1.1.1). + self.modsel = Gpio(gpio_modsel, Gpio.OUTPUT, 0) + + # ModPrs pin read pin MODPRESL from QSFP connector + self.modprs = Gpio(gpio_modprs, Gpio.INPUT, 0) + + # resolve device node name for I2C communication + devname = i2c_dev.dt_symbol_get_i2c_bus(devsymbol) + + # create an object to access I2C register interface + self.qsfp_regs = lib.i2c.make_i2cdev_regs_iface( + devname, # dev node name + 0x50, # start address according to SFF-8486 rev 4.9 chapter 7.6 + False, # use 7 bit address schema + 100, # timeout_ms + 1 # reg_addr_size + ) + + def _peek8(self, address): + """ + Helper method to read bytes from the I2C register interface. + + This helper returns None in case of failed communication + (e.g. missing or broken adapter). + """ + try: + return self.qsfp_regs.peek8(address) + except RuntimeError as err: + self.log.debug("Could not read QSFP register ({})".format(err)) + return None + + def _revision_compliance(self, status): + """ + Map the revison compliance status byte to a human readable string + according to SFF-8636 rev 2.9 table 6-3 + """ + assert isinstance(status, int) + assert 0 <= status <= 255 + if status > 0x08: + return "Reserved" + return QSFP_REVISION_COMPLIANCE[status] + + def is_available(self): + """ + Checks whether QSFP adapter is available by checking modprs pin + """ + return self.modprs.get() == 0 #modprs is active low + + def enable_i2c(self, enable): + """ + Enable or Disable I2C communication with QSFP module. Use with + care. Because X4xx uses an I2C switch to address the QSFP ports + there is no need to drive the modsel high (inactive). Disabled + I2C communication leads to unwanted result when query module + state even if the module reports availability. + """ + self.modsel.set("0" if enable else "1") #modsel is active low + + def adapter_id(self): + """ + Returns QSFP adapter ID as a byte (None if not present) + """ + return self._peek8(0) + + def adapter_id_name(self): + """ + Maps QSFP adapter ID to a human readable string according + to SFF-8436 rev 4.9 table 30 + """ + adapter_id = self.adapter_id() + if adapter_id is None: + return adapter_id + assert isinstance(adapter_id, int) + assert 0 <= adapter_id <= 255 + if adapter_id > 0x7F: + return "Vendor Specific" + if adapter_id > 0x16: + return "Reserved" + return QSFP_IDENTIFIERS[adapter_id] + + def status(self): + """ + Return the 2 byte QSFP adapter status according to SFF-8636 + rev 2.9 table 6-2 + """ + compliance = self._peek8(1) + status = self._peek8(2) + if compliance is None or status is None: + return None + assert isinstance(compliance, int) + assert isinstance(status, int) + return (compliance, status) + + def decoded_status(self): + """ + Decode the 2 status bytes of the QSFP adapter into a tuple + of human readable strings. See SFF-8436 rev 4.9 table 17 + """ + status = self.status() + if not status: + return None + return ( + self._revision_compliance(status[0]), + "Flat mem" if status[1] & 0b100 else "Paged mem", + "IntL asserted" if status[1] & 0b010 else "IntL not asserted", + "Data not ready" if status[1] & 0b001 else "Data ready" + ) + + def vendor_name(self): + """ + Return vendor name according to SFF-8436 rev 4.9 chapter 7.6.2.14 + """ + content = [self._peek8(i) for i in range(148, 163)] + + if all(content): # list must not contain any None values + # convert ASCII codes to string and strip whitespaces at the end + return "".join([chr(i) for i in content]).rstrip() + + return None + + def connector_type(self): + """ + Return connector type according to SFF-8029 rev 3.2 table 4-3 + """ + ctype = self._peek8(130) + if ctype is None: + return None + assert isinstance(ctype, int) + assert 0 <= ctype <= 255 + + if (0x0D < ctype < 0x20) or (0x24 < ctype < 0x80): + return "Reserved" + if ctype > 0x7F: + return "Vendor Specific" + return QSFP_CONNECTOR_TYPE[ctype] + + def info(self): + """ + Human readable string of important QSFP module information + """ + if self.is_available(): + status = self.decoded_status() + return "Vendor name: {}\n" \ + "id: {}\n" \ + "Connector type: {}\n" \ + "Compliance: {}\n" \ + "Status: {}".format( + self.vendor_name(), self.adapter_id_name(), + self.connector_type(), status[0], status[1:]) + + return "No module detected" + +def get_temp_sensor(sensor_names, reduce_fn=mean, log=None): + """ Get temperature sensor reading from X4xx. """ + temps = [] + try: + for sensor_name in sensor_names: + temp_raw = read_thermal_sensor_value( + sensor_name, 'in_temp_raw', 'iio', 'name') + temp_offset = read_thermal_sensor_value( + sensor_name, 'in_temp_offset', 'iio', 'name') + temp_scale = read_thermal_sensor_value( + sensor_name, 'in_temp_scale', 'iio', 'name') + # sysfs-bus-iio linux kernel API reports temp in milli deg C + # https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio + temp_in_deg_c = (temp_raw + temp_offset) * temp_scale / 1000 + temps.append(temp_in_deg_c) + except ValueError: + if log: + log.warning("Error when converting temperature value.") + temps = [-1] + except KeyError: + if log: + log.warning("Can't read %s temp sensor fron iio sub-system.", + str(sensor_name)) + temps = [-1] + return { + 'name': 'temperature', + 'type': 'REALNUM', + 'unit': 'C', + 'value': str(reduce_fn(temps)) + } diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_reference_pll.py b/mpm/python/usrp_mpm/periph_manager/x4xx_reference_pll.py new file mode 100644 index 000000000..4e3a26de2 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_reference_pll.py @@ -0,0 +1,339 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +LMK03328 driver for use with X4xx +""" + +from time import sleep +from usrp_mpm.chips import LMK03328 +from usrp_mpm.sys_utils.gpio import Gpio + +class LMK03328X4xx(LMK03328): + """ + X4xx-specific subclass of the Reference Clock PLL LMK03328 controls. + """ + def __init__(self, pll_regs_iface, log=None): + LMK03328.__init__(self, pll_regs_iface, log) + + self._pll_status_0 = Gpio('REFERENCE-CLOCK-PLL-STATUS-0', Gpio.INPUT) + self._pll_status_1 = Gpio('REFERENCE-CLOCK-PLL-STATUS-1', Gpio.INPUT) + self._pll_pwrdown_n = Gpio('REFERENCE-CLOCK-PLL-PWRDOWN', Gpio.OUTPUT, 1) + + self._reference_rates = None + + @property + def reference_rates(self): + """ + Gets a list of reference source rates indexed by [primary, secondary] + """ + return self._reference_rates + + @reference_rates.setter + def reference_rates(self, reference_rates): + """ + Sets a list of reference source rates indexed by [primary, secondary] + """ + assert len(reference_rates) == 2, 'Invalid number of reference rates' + self._reference_rates = reference_rates + + def init(self): + """ + Perform a soft reset and verify chip ID + """ + # Clear hard reset + self.reset(False, hard=True) + + # Enable sync mute + self.poke8(0x0C, 0xC8) + + # Trigger soft reset + self.reset(True, hard=False) + self.reset(False, hard=False) + + if not self.verify_chip_id(): + raise Exception("unable to locate LMK03328!") + + def reset(self, value=True, hard=False): + """ + Perform a hard reset from the GPIO pins or a soft reset from the LMK register + """ + if hard: + # The powerdown pin is active low + self._pll_pwrdown_n.set(not value) + else: + self.soft_reset(value) + + def get_status(self): + """ + Returns PLL lock and outgoing status indicators for the LMK03328 + """ + status_indicator_0 = self._pll_status_0.get() + status_indicator_1 = self._pll_status_1.get() + status_indicator = (status_indicator_1 << 1) | status_indicator_0 + return {'PLL1 lock': self.check_pll_locked(1), + 'PLL2 lock': self.check_pll_locked(2), + 'status indicator': status_indicator} + + def config(self, ref_select=2, brc_rate=25e6, usr_clk_rate=156.25e6, brc_select='PLL'): + """ + Configure the RPLL to generate the desired MGT Reference clock sources + using the specified internal BRC. + ref_select - the reference source to use (primary=1, secondary=2) + brc_rate - specifies the desired rate of the output BRC + usr_clk_rate - specifies the desired rate to configure PLL1 + brc_select - specifies whether the BRC out should be from the PLL ('PLL') or + a passthrough of the primary reference signal ('bypass') + """ + def calculate_out7_mux(brc_select): + """ + Returns the OUT7 Mux select register value based on the chosen BRC source. + Note that OUT7 is wired to the InternalRef clock which is used as the default + reference clock source. + """ + return {'bypass': 0x98, 'PLL': 0x58}[brc_select] + def calculate_out7_div(brc_rate): + """ Returns the OUT7 Divider register value based on the chosen BRC rate """ + return {25e6: 0x31, 125e6: 0x09}[brc_rate] + def calculate_pll2_input_select(ref_select): + """ Returns the reference mux register value based on which reference should be used """ + assert ref_select in (1, 2) + return {1: 0x5B, 2: 0x7F}[ref_select] + def calculate_pll2_n_div(ref_rate): + """ Returns the PLL2 N div value based on the rate of the reference source """ + return {25e6: 0x00C8, 100e6: 0x0032}[ref_rate] + def calculate_pll1_post_div(usr_clk_rate): + """ Returns the PLL1 post div value based the usr_clk_rate """ + assert usr_clk_rate in (156.25e6, 125e6, 312.5e6, 161.1328125e6) + return { + 156.25e6: 0x0E, + 125e6: 0x0E, + 312.5e6: 0x0E, + 161.1328125e6: 0x1E, + }[usr_clk_rate] + def calculate_pll1_n_div(usr_clk_rate): + """ Returns the PLL1 N div value based the usr_clk_rate """ + assert usr_clk_rate in (156.25e6, 125e6, 312.5e6, 161.1328125e6) + return { + 156.25e6: 0x0339, + 125e6: 0x0339, + 312.5e6: 0x0032, + 161.1328125e6: 0x0339, + }[usr_clk_rate] + def calculate_pll1_m_div(usr_clk_rate): + """ Returns the PLL1 M div value based the usr_clk_rate """ + assert usr_clk_rate in (156.25e6, 125e6, 312.5e6, 161.1328125e6) + return { + 156.25e6: 0x0F, + 125e6: 0x0F, + 312.5e6: 0x00, + 161.1328125e6: 0x0F, + }[usr_clk_rate] + def calculate_pll_select(usr_clk_rate): + """ Returns the PLL selection based on the usr_clk_rate """ + assert usr_clk_rate in (156.25e6, 125e6) + return {156.25e6: 2, 125e6: 2}[usr_clk_rate] + def get_register_from_pll(pll_selection, addr): + """ Returns the value to write to a specified register given + the desired PLL selection. """ + assert pll_selection in (1, 2) + assert addr in (0x22, 0x29) + return {0x22: [0x00, 0x80], 0x29: [0x10, 0x50]}[addr][pll_selection-1] + def calculate_out_div(usr_clk_rate): + """ Returns the output divider for a given clock rate """ + assert usr_clk_rate in (156.25e6, 125e6) + return {156.25e6: 0x07, 125e6: 0x09}[usr_clk_rate] + + if self._reference_rates is None: + self.log.error('Cannot config reference PLL until the reference sources are set.') + raise RuntimeError('Cannot config reference PLL until the reference sources are set.') + if ref_select not in (1, 2): + raise RuntimeError('Selected reference source {} is invalid'.format(ref_select)) + ref_rate = self._reference_rates[ref_select-1] + if ref_rate not in (25e6, 100e6): + raise RuntimeError('Selected reference rate {} Hz is invalid'.format(ref_rate)) + if brc_select not in ('bypass', 'PLL'): + raise RuntimeError('Selected BRC source {} is invalid'.format(brc_select)) + if brc_rate not in (25e6, 125e6): + raise RuntimeError('Selected BRC rate {} Hz is invalid'.format(brc_rate)) + if brc_select == 'bypass': + # 'bypass' sends the primary reference directly to out7 + actual_brc_rate = self._reference_rates[0] + if actual_brc_rate != brc_rate: + self.log.error('The specified BRC rate does not match the actual ' + 'rate of the primary ref in bypass mode.') + raise RuntimeError('The specified BRC rate does not match the actual ' + 'rate of the primary ref in bypass mode.') + if usr_clk_rate not in (156.25e6, 125e6): + raise RuntimeError('Selected RPLL clock rate {} Hz is not supported'.format(usr_clk_rate)) + + self.log.trace("Configuring RPLL to ref:{}, brc:{} {} Hz, clock rate:{}" + .format(ref_select, brc_select, brc_rate, usr_clk_rate)) + # Config + pll2_input_mux = calculate_pll2_input_select(ref_select) + pll2_n_div = calculate_pll2_n_div(ref_rate) + pll1_post_div = calculate_pll1_post_div(usr_clk_rate) + pll1_n_div = calculate_pll1_n_div(usr_clk_rate) + pll1_m_div = calculate_pll1_m_div(usr_clk_rate) + pll_select = calculate_pll_select(usr_clk_rate) + out_div = calculate_out_div(usr_clk_rate) + out7_mux = calculate_out7_mux(brc_select) + out7_div = calculate_out7_div(brc_rate) + + self.pokes8(( + (0x0C, 0xDF), + (0x0D, 0x00), + (0x0E, 0x00), + (0x0F, 0x00), + (0x10, 0x00), + (0x11, 0x00), + (0x12, 0x00), + (0x13, 0x00), + (0x14, 0xFF), + (0x15, 0xFF), + (0x16, 0xFF), + (0x17, 0x00), # Status 0/1 mute control is disabled. Both status always ON. + (0x18, 0x00), + (0x19, 0x55), + (0x1A, 0x00), + (0x1B, 0x58), + (0x1C, 0x58), + (0x1D, 0x8F), + (0x1E, 0x01), + (0x1F, 0x00), + (0x20, 0x00), + (0x21, 0x00), + (0x22, get_register_from_pll(pll_select, 0x22)), + (0x23, 0x20), + (0x24, out_div), + (0x25, 0xD0), + (0x26, 0x00), + (0x27, 0xD0), + (0x28, 0x09), + (0x29, get_register_from_pll(pll_select, 0x29)), + (0x2A, out_div), + (0x2B, out7_mux), + (0x2C, out7_div), + (0x2D, 0x0A), # Disable all PLL divider status outputs. Both status pins are set to normal operation. + (0x2E, 0x00), # Disable all PLL divider status outputs. + (0x2F, 0x00), # Disable all PLL divider status outputs. + (0x30, 0xFF), # Hidden register. Value from TICS software. + (0x31, 0x0A), # set both status slew rate to slow (2.1 ns) + (0x32, pll2_input_mux), + (0x33, 0x03), + (0x34, 0x00), + (0x35, pll1_m_div), + (0x36, 0x00), + (0x37, 0x00), + (0x38, pll1_post_div), # PLL1 enabled, PLL1 output reset sync enable + (0x39, 0x08), + (0x3A, (pll1_n_div & 0x0F00) >> 8), # PLL1 N Divider [11:8] + (0x3B, (pll1_n_div & 0x00FF) >> 0), # PLL1 N Divider [7:0] + (0x3C, 0x00), + (0x3D, 0x00), + (0x3E, 0x00), + (0x3F, 0x00), + (0x40, 0x00), + (0x41, 0x01), + (0x42, 0x0C), + (0x43, 0x08), # PLL1 loop filter R2 = 735 ohms + (0x44, 0x00), # PLL1 loop filter C1 = 5 pF + (0x45, 0x00), # PLL1 loop filter R3 = 18 ohms + (0x46, 0x00), # PLL1 loop filter C3 = 0 pF + (0x47, 0x0E), + (0x48, 0x08), + (0x49, (pll2_n_div & 0x0F00) >> 8), # PLL2 N Divider [11:8] + (0x4A, (pll2_n_div & 0x00FF) >> 0), # PLL2 N Divider [7:0] + (0x4B, 0x00), + (0x4C, 0x00), + (0x4D, 0x00), + (0x4E, 0x00), + (0x4F, 0x00), + (0x50, 0x01), + (0x51, 0x0C), + (0x52, 0x08), + (0x53, 0x00), + (0x54, 0x00), + (0x55, 0x00), + (0x56, 0x08), + (0x57, 0x00), + (0x58, 0x00), + (0x59, 0xDE), + (0x5A, 0x01), + (0x5B, 0x18), + (0x5C, 0x01), + (0x5D, 0x4B), + (0x5E, 0x01), + (0x5F, 0x86), + (0x60, 0x01), + (0x61, 0xBE), + (0x62, 0x01), + (0x63, 0xFE), + (0x64, 0x02), + (0x65, 0x47), + (0x66, 0x02), + (0x67, 0x9E), + (0x68, 0x00), + (0x69, 0x00), + (0x6A, 0x05), + (0x6B, 0x0F), + (0x6C, 0x0F), + (0x6D, 0x0F), + (0x6E, 0x0F), + (0x6F, 0x00), + (0x70, 0x00), + (0x71, 0x00), + (0x72, 0x00), + (0x73, 0x08), + (0x74, 0x19), + (0x75, 0x00), + (0x76, 0x03), # PLL1 uses 2nd order loop filter recommended for integer PLL mode. + (0x77, 0x01), + (0x78, 0x00), + (0x79, 0x0F), + (0x7A, 0x0F), + (0x7B, 0x0F), + (0x7C, 0x0F), + (0x7D, 0x00), + (0x7E, 0x00), + (0x7F, 0x00), + (0x80, 0x00), + (0x81, 0x08), + (0x82, 0x19), + (0x83, 0x00), + (0x84, 0x03), # PLL2 uses 2nd order loop filter recommended for integer PLL mode. + (0x85, 0x01), + (0x86, 0x00), + (0x87, 0x00), + (0x88, 0x00), + (0x89, 0x10), + (0x8A, 0x00), + (0x8B, 0x00), + (0x8C, 0x00), + (0x8D, 0x00), + (0x8E, 0x00), + (0x8F, 0x00), + (0x90, 0x00), + (0x91, 0x00), + (0xA9, 0x40), + (0xAC, 0x24), + (0xAD, 0x00), + (0x0C, 0x5F), # Initiate VCO calibration + (0x0C, 0xDF), + )) + # wait for VCO calibration to be done and PLL to lock + sleep(0.5) + # Reset all output and PLL post dividers + self.pokes8(( + (0x0C, 0x9F), + (0x0C, 0xDF) + )) + + # Check for Lock + if not self.check_pll_locked(1): + raise RuntimeError('PLL1 did not lock!') + if not self.check_pll_locked(2): + raise RuntimeError('PLL2 did not lock!') + self.log.trace("PLLs are locked!") diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_rfdc_ctrl.py b/mpm/python/usrp_mpm/periph_manager/x4xx_rfdc_ctrl.py new file mode 100644 index 000000000..44c4a32e2 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_rfdc_ctrl.py @@ -0,0 +1,480 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +X400 RFDC Control Module +""" + +import ast +from collections import OrderedDict +from usrp_mpm import lib # Pulls in everything from C++-land +from usrp_mpm.periph_manager.x4xx_rfdc_regs import RfdcRegsControl +from usrp_mpm.rpc_server import no_rpc + +# Map the interpolation/decimation factor to fabric words. +# Keys: is_dac (False -> ADC, True -> DAC) and factor +FABRIC_WORDS_ARRAY = { # [is_dac][factor] + False: {0: 16, 1: 16, 2: 8, 4: 4, 8: 2}, # ADC + True: {0: -1, 1: -1, 2: 16, 4: 8, 8: 4} # DAC +} + +RFDC_DEVICE_ID = 0 + +class X4xxRfdcCtrl: + """ + Control class for the X4xx's RFDC + + """ + # Label for RFDC UIO + rfdc_regs_label = "rfdc-regs" + # Describes the mapping of ADC/DAC Tiles and Blocks to DB Slot IDs + # Follows the below structure: + # <slot_idx> + # 'adc': [ (<tile_number>, <block_number>), ... ] + # 'dac': [ (<tile_number>, <block_number>), ... ] + RFDC_DB_MAP = [ + { + 'adc': [(0, 1), (0, 0)], + 'dac': [(0, 0), (0, 1)], + }, + { + 'adc': [(2, 1), (2, 0)], + 'dac': [(1, 0), (1, 1)], + }, + ] + + # Maps all possible master_clock_rate (data clk rate * data SPC) values to the + # corresponding sample rate, expected FPGA decimation, whether to configure + # the SPLL in legacy mode (which uses a different divider), and whether half-band + # resampling is used. + # Using an OrderedDict to use the first rates as a preference for the default + # rate for its corresponding decimation. + master_to_sample_clk = OrderedDict({ + # MCR: (SPLL, decimation, legacy mode, half-band resampling) + 122.88e6*4: (2.94912e9, 2, False, False), # RF (1M-8G) + 122.88e6*2: (2.94912e9, 2, False, True), # RF (1M-8G) + 122.88e6*1: (2.94912e9, 8, False, False), # RF (1M-8G) + 125e6*4: (3.00000e9, 2, False, False), # RF (1M-8G) + 200e6: (3.00000e9, 4, True, False), # RF (Legacy Mode) + }) + + + def __init__(self, get_spll_freq, log): + self.log = log.getChild('RFDC') + self._get_spll_freq = get_spll_freq + self._rfdc_regs = RfdcRegsControl(self.rfdc_regs_label, self.log) + self._rfdc_ctrl = lib.rfdc.rfdc_ctrl() + self._rfdc_ctrl.init(RFDC_DEVICE_ID) + + self.set_cal_frozen(1, 0, "both") + self.set_cal_frozen(1, 1, "both") + + @no_rpc + def unset_cbs(self): + """ + Removes any stored references to our owning X4xx class instance + """ + self._get_spll_freq = None + + ########################################################################### + # Public APIs (not available as MPM RPC calls) + ########################################################################### + @no_rpc + def set_reset(self, reset=True): + """ + Resets the RFDC FPGA components or takes them out of reset. + """ + if reset: + # Assert RFDC AXI-S, filters and associated gearbox reset. + self._rfdc_regs.set_reset_adc_dac_chains(reset=True) + self._rfdc_regs.log_status() + # Assert Radio clock PLL reset + self._rfdc_regs.set_reset_mmcm(reset=True) + # Resetting the MMCM will automatically disable clock buffers + return + + # Take upstream MMCM out of reset + self._rfdc_regs.set_reset_mmcm(reset=False) + + # Once the MMCM has locked, enable driving the clocks + # to the rest of the design. Poll lock status for up + # to 1 ms + self._rfdc_regs.wait_for_mmcm_locked(timeout=0.001) + self._rfdc_regs.set_gated_clock_enables(value=True) + + # De-assert RF signal chain reset + self._rfdc_regs.set_reset_adc_dac_chains(reset=False) + + # Restart tiles in XRFdc + # All ADC Tiles + if not self._rfdc_ctrl.reset_tile(-1, False): + self.log.warning('Error starting up ADC tiles') + # All DAC Tiles + if not self._rfdc_ctrl.reset_tile(-1, True): + self.log.warning('Error starting up DAC tiles') + + # Set sample rate for all active tiles + active_converters = set() + for db_idx, db_info in enumerate(self.RFDC_DB_MAP): + db_rfdc_resamp, _ = self._rfdc_regs.get_rfdc_resampling_factor(db_idx) + for converter_type, tile_block_set in db_info.items(): + for tile, block in tile_block_set: + is_dac = converter_type != 'adc' + active_converter_tuple = (tile, block, db_rfdc_resamp, is_dac) + active_converters.add(active_converter_tuple) + for tile, block, resampling_factor, is_dac in active_converters: + self._rfdc_ctrl.reset_mixer_settings(tile, block, is_dac) + self._rfdc_ctrl.set_sample_rate(tile, is_dac, self._get_spll_freq()) + self._set_interpolation_decimation(tile, block, is_dac, resampling_factor) + + self._rfdc_regs.log_status() + + # Set RFDC NCO reset event source to analog SYSREF + for tile, block, _, is_dac in active_converters: + self._rfdc_ctrl.set_nco_event_src(tile, block, is_dac) + + + @no_rpc + def sync(self): + """ + Multi-tile Synchronization on both ADC and DAC + """ + # These numbers are determined from the procedure mentioned in + # PG269 section "Advanced Multi-Tile Synchronization API use". + adc_latency = 1228 # ADC delay in sample clocks + dac_latency = 800 # DAC delay in sample clocks + + # Ideally, this would be a set to avoiding duplicate indices, + # but we need to use a list for compatibility with the rfdc_ctrl + # C++ interface (std::vector) + adc_tiles_to_sync = [] + dac_tiles_to_sync = [] + + rfdc_map = self.RFDC_DB_MAP + for db_id in rfdc_map: + for converter_type, tile_block_set in db_id.items(): + for tile, _ in tile_block_set: + if converter_type == 'adc': + if tile not in adc_tiles_to_sync: + adc_tiles_to_sync.append(tile) + else: # dac + if tile not in dac_tiles_to_sync: + dac_tiles_to_sync.append(tile) + + self._rfdc_ctrl.sync_tiles(adc_tiles_to_sync, False, adc_latency) + self._rfdc_ctrl.sync_tiles(dac_tiles_to_sync, True, dac_latency) + + # We expect all sync'd tiles to have equal latencies + # Sets don't add duplicates, so we can use that to look + # for erroneous tiles + adc_tile_latency_set = set() + for tile in adc_tiles_to_sync: + adc_tile_latency_set.add( + self._rfdc_ctrl.get_tile_latency(tile, False)) + if len(adc_tile_latency_set) != 1: + raise RuntimeError("ADC tiles failed to sync properly") + + dac_tile_latency_set = set() + for tile in dac_tiles_to_sync: + dac_tile_latency_set.add( + self._rfdc_ctrl.get_tile_latency(tile, True)) + if len(dac_tile_latency_set) != 1: + raise RuntimeError("DAC tiles failed to sync properly") + + @no_rpc + def get_default_mcr(self): + """ + Gets the default master clock rate based on FPGA decimation + """ + fpga_decimation, fpga_halfband = self._rfdc_regs.get_rfdc_resampling_factor(0) + for master_clock_rate in self.master_to_sample_clk: + _, decimation, _, halfband = self.master_to_sample_clk[master_clock_rate] + if decimation == fpga_decimation and fpga_halfband == halfband: + return master_clock_rate + raise RuntimeError('No master clock rate acceptable for current fpga ' + 'with decimation of {}'.format(fpga_decimation)) + + @no_rpc + def get_dsp_bw(self): + """ + Return the bandwidth encoded in the RFdc registers. + + Note: This is X4xx-specific, not RFdc-specific. But this class owns the + access to RfdcRegsControl, and the bandwidth is strongly related to the + RFdc settings. + """ + return self._rfdc_regs.get_fabric_dsp_info(0)[0] + + @no_rpc + def get_rfdc_resampling_factor(self, db_idx): + """ + Returns a tuple resampling_factor, halfbands. + + See RfdcRegsControl.get_rfdc_resampling_factor(). + """ + return self._rfdc_regs.get_rfdc_resampling_factor(db_idx) + + + ########################################################################### + # Public APIs that get exposed as MPM RPC calls + ########################################################################### + def rfdc_set_nco_freq(self, direction, slot_id, channel, freq): + """ + Sets the RFDC NCO Frequency for the specified channel + """ + converters = self._find_converters(slot_id, direction, channel) + assert len(converters) == 1 + (tile_id, block_id, is_dac) = converters[0] + + if not self._rfdc_ctrl.set_if(tile_id, block_id, is_dac, freq): + raise RuntimeError("Error setting RFDC IF Frequency") + return self._rfdc_ctrl.get_nco_freq(tile_id, block_id, is_dac) + + def rfdc_get_nco_freq(self, direction, slot_id, channel): + """ + Gets the RFDC NCO Frequency for the specified channel + """ + converters = self._find_converters(slot_id, direction, channel) + assert len(converters) == 1 + (tile_id, block_id, is_dac) = converters[0] + + return self._rfdc_ctrl.get_nco_freq(tile_id, block_id, is_dac) + + ### ADC cal ############################################################### + def set_cal_frozen(self, frozen, slot_id, channel): + """ + Set the freeze state for the ADC cal blocks + + Usage: + > set_cal_frozen <frozen> <slot_id> <channel> + + <frozen> should be 0 to unfreeze the calibration blocks or 1 to freeze them. + """ + for tile_id, block_id, _ in self._find_converters(slot_id, "rx", channel): + self._rfdc_ctrl.set_cal_frozen(tile_id, block_id, frozen) + + def get_cal_frozen(self, slot_id, channel): + """ + Get the freeze states for each ADC cal block in the channel + + Usage: + > get_cal_frozen <slot_id> <channel> + """ + return [ + 1 if self._rfdc_ctrl.get_cal_frozen(tile_id, block_id) else 0 + for tile_id, block_id, is_dac in self._find_converters(slot_id, "rx", channel) + ] + + def set_cal_coefs(self, channel, slot_id, cal_block, coefs): + """ + Manually override calibration block coefficients. You probably don't need to use this. + """ + self.log.trace( + "Setting ADC cal coefficients for channel={} slot_id={} cal_block={}".format( + channel, slot_id, cal_block)) + for tile_id, block_id, _ in self._find_converters(slot_id, "rx", channel): + self._rfdc_ctrl.set_adc_cal_coefficients( + tile_id, block_id, cal_block, ast.literal_eval(coefs)) + + def get_cal_coefs(self, channel, slot_id, cal_block): + """ + Manually retrieve raw coefficients for the ADC calibration blocks. + + Usage: + > get_cal_coefs <channel, 0-1> <slot_id, 0-1> <cal_block, 0-3> + e.g. + > get_cal_coefs 0 1 3 + Retrieves the coefficients for the TSCB block on channel 0 of DB 1. + + Valid values for cal_block are: + 0 - OCB1 (Unaffected by cal freeze) + 1 - OCB2 (Unaffected by cal freeze) + 2 - GCB + 3 - TSCB + """ + self.log.trace( + "Getting ADC cal coefficients for channel={} slot_id={} cal_block={}".format( + channel, slot_id, cal_block)) + result = [] + for tile_id, block_id, _ in self._find_converters(slot_id, "rx", channel): + result.append(self._rfdc_ctrl.get_adc_cal_coefficients(tile_id, block_id, cal_block)) + return result + + ### DAC mux + def set_dac_mux_data(self, i_val, q_val): + """ + Sets the data which is muxed into the DACs when the DAC mux is enabled + + Usage: + > set_dac_mux_data <I> <Q> + e.g. + > set_dac_mux_data 123 456 + """ + self._rfdc_regs.set_cal_data(i_val, q_val) + + def set_dac_mux_enable(self, channel, enable): + """ + Sets whether the DAC mux is enabled for a given channel + + Usage: + > set_dac_mux_enable <channel, 0-3> <enable, 1=enabled> + e.g. + > set_dac_mux_enable 1 0 + """ + self._rfdc_regs.set_cal_enable(channel, bool(enable)) + + ### ADC thresholds + def setup_threshold(self, slot_id, channel, threshold_idx, mode, delay, under, over): + """ + Configure the given ADC threshold block. + + Usage: + > setup_threshold <slot_id> <channel> <threshold_idx> <mode> <delay> <under> <over> + + slot_id: Slot ID to configure, 0 or 1 + channel: Channel on the slot to configure, 0 or 1 + threshold_idx: Threshold block index, 0 or 1 + mode: Mode to configure, one of ["sticky_over", "sticky_under", "hysteresis"] + delay: In hysteresis mode, number of samples before clearing flag. + under: 0-16384, ADC codes to set the "under" threshold to + over: 0-16384, ADC codes to set the "over" threshold to + """ + for tile_id, block_id, _ in self._find_converters(slot_id, "rx", channel): + THRESHOLDS = { + 0: lib.rfdc.threshold_id_options.THRESHOLD_0, + 1: lib.rfdc.threshold_id_options.THRESHOLD_1, + } + MODES = { + "sticky_over": lib.rfdc.threshold_mode_options.TRSHD_STICKY_OVER, + "sticky_under": lib.rfdc.threshold_mode_options.TRSHD_STICKY_UNDER, + "hysteresis": lib.rfdc.threshold_mode_options.TRSHD_HYSTERESIS, + } + if mode not in MODES: + raise RuntimeError( + f"Mode {mode} is not one of the allowable modes {list(MODES.keys())}") + if threshold_idx not in THRESHOLDS: + raise RuntimeError("threshold_idx must be 0 or 1") + delay = int(delay) + under = int(under) + over = int(over) + assert 0 <= under <= 16383 + assert 0 <= over <= 16383 + self._rfdc_ctrl.set_threshold_settings( + tile_id, block_id, + lib.rfdc.threshold_id_options.THRESHOLD_0, + MODES[mode], + delay, + under, + over) + + def get_threshold_status(self, slot_id, channel, threshold_idx): + """ + Read the threshold status bit for the given threshold block from the device. + + Usage: + > get_threshold_status <slot_id> <channel> <threshold_idx> + e.g. + > get_threshold_status 0 1 0 + """ + return self._rfdc_regs.get_threshold_status(slot_id, channel, threshold_idx) != 0 + + + ########################################################################### + # Private helpers (note: x4xx_db_iface calls into those) + ########################################################################### + def _set_interpolation_decimation(self, tile, block, is_dac, factor): + """ + Set the provided interpolation/decimation factor to the + specified ADC/DAC tile, block + + Only gets called from set_reset_rfdc(). + """ + # Map the interpolation/decimation factor to fabric words. + # Keys: is_dac (False -> ADC, True -> DAC) and factor + # Disable FIFO + self._rfdc_ctrl.set_data_fifo_state(tile, is_dac, False) + # Define fabric rate based on given factor. + fab_words = FABRIC_WORDS_ARRAY[is_dac].get(int(factor)) + if fab_words == -1: + raise RuntimeError('Unsupported dec/int factor in RFDC') + # Define dec/int constant based on integer factor + if factor == 0: + int_dec = lib.rfdc.interp_decim_options.INTERP_DECIM_OFF + elif factor == 1: + int_dec = lib.rfdc.interp_decim_options.INTERP_DECIM_1X + elif factor == 2: + int_dec = lib.rfdc.interp_decim_options.INTERP_DECIM_2X + elif factor == 4: + int_dec = lib.rfdc.interp_decim_options.INTERP_DECIM_4X + elif factor == 8: + int_dec = lib.rfdc.interp_decim_options.INTERP_DECIM_8X + else: + raise RuntimeError('Unsupported dec/int factor in RFDC') + # Update tile, block settings... + self.log.debug( + "Setting %s for %s tile %d, block %d to %dx", + ('interpolation' if is_dac else 'decimation'), + 'DAC' if is_dac else 'ADC', tile, block, factor) + if is_dac: + # Set interpolation + self._rfdc_ctrl.set_interpolation_factor(tile, block, int_dec) + self.log.trace( + " interpolation: %s", + self._rfdc_ctrl.get_interpolation_factor(tile, block).name) + # Set fabric write rate + self._rfdc_ctrl.set_data_write_rate(tile, block, fab_words) + self.log.trace( + " Read words: %d", + self._rfdc_ctrl.get_data_write_rate(tile, block, True)) + else: # ADC + # Set decimation + self._rfdc_ctrl.set_decimation_factor(tile, block, int_dec) + self.log.trace( + " Decimation: %s", + self._rfdc_ctrl.get_decimation_factor(tile, block).name) + # Set fabric read rate + self._rfdc_ctrl.set_data_read_rate(tile, block, fab_words) + self.log.trace( + " Read words: %d", + self._rfdc_ctrl.get_data_read_rate(tile, block, False)) + # Clear interrupts + self._rfdc_ctrl.clear_data_fifo_interrupts(tile, block, is_dac) + # Enable FIFO + self._rfdc_ctrl.set_data_fifo_state(tile, is_dac, True) + + + def _find_converters(self, slot, direction, channel): + """ + Returns a list of (tile_id, block_id, is_dac) tuples describing + the data converters associated with a given channel and direction. + """ + if direction not in ('rx', 'tx', 'both'): + self.log.error('Invalid direction "{}". Cannot find ' + 'associated data converters'.format(direction)) + raise RuntimeError('Invalid direction "{}". Cannot find ' + 'associated data converters'.format(direction)) + if str(channel) not in ('0', '1', 'both'): + self.log.error('Invalid channel "{}". Cannot find ' + 'associated data converters'.format(channel)) + raise RuntimeError('Invalid channel "{}". Cannot find ' + 'associated data converters'.format(channel)) + data_converters = [] + rfdc_map = self.RFDC_DB_MAP[slot] + + if direction in ('rx', 'both'): + if str(channel) == '0' or str(channel) == 'both': + (tile_id, block_id) = rfdc_map['adc'][0] + data_converters.append((tile_id, block_id, False)) + if str(channel) == '1' or str(channel) == 'both': + (tile_id, block_id) = rfdc_map['adc'][1] + data_converters.append((tile_id, block_id, False)) + if direction in ('tx', 'both'): + if str(channel) == '0' or str(channel) == 'both': + (tile_id, block_id) = rfdc_map['dac'][0] + data_converters.append((tile_id, block_id, True)) + if str(channel) == '1' or str(channel) == 'both': + (tile_id, block_id) = rfdc_map['dac'][1] + data_converters.append((tile_id, block_id, True)) + return data_converters diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_rfdc_regs.py b/mpm/python/usrp_mpm/periph_manager/x4xx_rfdc_regs.py new file mode 100644 index 000000000..95f3cc007 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_rfdc_regs.py @@ -0,0 +1,263 @@ +# +# Copyright 2021 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +X4xx RFDC register control +""" + +import time +from usrp_mpm.sys_utils.uio import UIO + +class RfdcRegsControl: + """ + Control the FPGA RFDC registers external to the XRFdc API + """ + # pylint: disable=bad-whitespace + IQ_SWAP_OFFSET = 0x10000 + MMCM_RESET_BASE_OFFSET = 0x11000 + RF_RESET_CONTROL_OFFSET = 0x12000 + RF_RESET_STATUS_OFFSET = 0x12008 + RF_STATUS_OFFSET = 0x13000 + FABRIC_DSP_INFO_OFFSET = 0x13008 + CAL_DATA_OFFSET = 0x14000 + CAL_ENABLE_OFFSET = 0x14008 + THRESHOLD_STATUS_OFFSET = 0x15000 + RF_PLL_CONTROL_OFFSET = 0x16000 + RF_PLL_STATUS_OFFSET = 0x16008 + # pylint: enable=bad-whitespace + + def __init__(self, label, log): + self.log = log.getChild("RfdcRegs") + self.regs = UIO( + label=label, + read_only=False + ) + self.poke32 = self.regs.poke32 + self.peek32 = self.regs.peek32 + + # Index corresponds to dboard number. + self._converter_chains_in_reset = True + + def get_threshold_status(self, slot_id, channel, threshold_idx): + """ + Retrieves the status bit for the given threshold block + """ + BITMASKS = { + (0, 0, 0): 0x04, + (0, 0, 1): 0x08, + (0, 1, 0): 0x01, + (0, 1, 1): 0x02, + (1, 0, 0): 0x400, + (1, 0, 1): 0x800, + (1, 1, 0): 0x100, + (1, 1, 1): 0x200, + } + assert (slot_id, channel, threshold_idx) in BITMASKS + status = self.peek(self.THRESHOLD_STATUS_OFFSET) + status_bool = (status & BITMASKS[(slot_id, channel, threshold_idx)]) != 0 + return 1 if status_bool else 0 + + def set_cal_data(self, i, q): + assert 0 <= i < 2**16 + assert 0 <= q < 2**16 + self.poke(self.CAL_DATA_OFFSET, (q << 16) | i) + + def set_cal_enable(self, channel, enable): + assert 0 <= channel <= 3 + assert enable in [False, True] + en = self.peek(self.CAL_ENABLE_OFFSET) + bit_offsets = { + 0: 0, + 1: 1, + 2: 4, + 3: 5, + } + en_mask = 1 << bit_offsets[channel] + en = en & ~en_mask + self.poke(self.CAL_ENABLE_OFFSET, en | (en_mask if enable else 0)) + + def enable_iq_swap(self, enable, db_id, block_id, is_dac): + iq_swap_bit = (int(is_dac) * 8) + (db_id * 4) + block_id + + # Write IQ swap bit with a mask + reg_val = self.peek(self.IQ_SWAP_OFFSET) + reg_val = (reg_val & ~(1 << iq_swap_bit)) \ + | (enable << iq_swap_bit) + self.poke(self.IQ_SWAP_OFFSET, reg_val) + + def set_reset_mmcm(self, reset=True): + if reset: + # Put the MMCM in reset (active low) + self.poke(self.MMCM_RESET_BASE_OFFSET, 0) + else: + # Take the MMCM out of reset + self.poke(self.MMCM_RESET_BASE_OFFSET, 1) + + def wait_for_mmcm_locked(self, timeout=0.001): + """ + Wait for MMCM to come to a stable locked state. + The datasheet specifies a 100us max lock time + """ + DATA_CLK_PLL_LOCKED = 1 << 20 + + POLL_SLEEP = 0.0002 + for _ in range(int(timeout / POLL_SLEEP)): + time.sleep(POLL_SLEEP) + status = self.peek(self.RF_PLL_STATUS_OFFSET) + if status & DATA_CLK_PLL_LOCKED: + self.log.trace("RF MMCM lock detected.") + return + self.log.error("MMCM failed to lock in the expected time.") + raise RuntimeError("MMCM failed to lock within the expected time.") + + def set_gated_clock_enables(self, value=True): + """ + Controls the clock enable for data_clk and + data_clk_2x + """ + ENABLE_DATA_CLK = 1 + ENABLE_DATA_CLK_2X = 1 << 4 + ENABLE_RF_CLK = 1 << 8 + ENABLE_RF_CLK_2X = 1 << 12 + if value: + # Enable buffers gating the clocks + self.poke(self.RF_PLL_CONTROL_OFFSET, + ENABLE_DATA_CLK | + ENABLE_DATA_CLK_2X | + ENABLE_RF_CLK | + ENABLE_RF_CLK_2X + ) + else: + # Disable clock buffers to have clocks gated. + self.poke(self.RF_PLL_CONTROL_OFFSET, 0) + + def get_fabric_dsp_info(self, dboard): + """ + Read the DSP information register and returns the + DSP bandwidth, rx channel count and tx channel count + """ + # Offsets + DSP_BW = 0 + 16*dboard + DSP_RX_CNT = 12 + 16*dboard + DSP_TX_CNT = 14 + 16*dboard + # Masks + DSP_BW_MSK = 0xFFF + DSP_RX_CNT_MSK = 0x3 + DSP_TX_CNT_MSK = 0x3 + + dsp_info = self.peek(self.FABRIC_DSP_INFO_OFFSET) + self.log.trace("Fabric DSP for dboard %d...", dboard) + dsp_bw = (dsp_info >> DSP_BW) & DSP_BW_MSK + self.log.trace(" Bandwidth (MHz): %d", dsp_bw) + dsp_rx_cnt = (dsp_info >> DSP_RX_CNT) & DSP_RX_CNT_MSK + self.log.trace(" Rx channel count: %d", dsp_rx_cnt) + dsp_tx_cnt = (dsp_info >> DSP_TX_CNT) & DSP_TX_CNT_MSK + self.log.trace(" Tx channel count: %d", dsp_tx_cnt) + + return [dsp_bw, dsp_rx_cnt, dsp_tx_cnt] + + def get_rfdc_resampling_factor(self, dboard): + """ + Returns the appropriate decimation/interpolation factor to set in the RFDC. + """ + # DSP vs. RFDC decimation/interpolation dictionary + # Key: bandwidth in MHz + # Value: (RFDC resampling factor, is Half-band resampling used?) + RFDC_RESAMPLING_FACTOR = { + 100: (8, False), # 100 MHz BW requires 8x RFDC resampling + 200: (2, True), # 200 MHz BW requires 2x RFDC resampling + # (400 MHz RFDC DSP used w/ half-band resampling) + 400: (2, False) # 400 MHz BW requires 2x RFDC resampling + } + dsp_bw, _, _ = self.get_fabric_dsp_info(dboard) + # When no RF fabric DSP is present (dsp_bw = 0), MPM should + # simply use the default RFDC resampling factor (400 MHz). + if dsp_bw in RFDC_RESAMPLING_FACTOR: + rfdc_resampling_factor, halfband = RFDC_RESAMPLING_FACTOR[dsp_bw] + else: + rfdc_resampling_factor, halfband = RFDC_RESAMPLING_FACTOR[400] + self.log.trace(" Using default resampling!") + self.log.trace(" RFDC resampling: %d", rfdc_resampling_factor) + return (rfdc_resampling_factor, halfband) + + def set_reset_adc_dac_chains(self, reset=True): + """ Resets or enables the ADC and DAC chain for the given dboard """ + + def _wait_for_done(done_bit, timeout=5): + """ + Wait for the specified sequence done bit when resetting or + enabling an ADC or DAC chain. Throws an error on timeout. + """ + status = self.peek(self.RF_RESET_STATUS_OFFSET) + if (status & done_bit): + return + for _ in range(0, timeout): + time.sleep(0.001) # 1 ms + status = self.peek(self.RF_RESET_STATUS_OFFSET) + if (status & done_bit): + return + self.log.error("Timeout while resetting or enabling ADC/DAC chains.") + raise RuntimeError("Timeout while resetting or enabling ADC/DAC chains.") + + # CONTROL OFFSET + ADC_RESET = 1 << 4 + DAC_RESET = 1 << 8 + # STATUS OFFSET + ADC_SEQ_DONE = 1 << 7 + DAC_SEQ_DONE = 1 << 11 + + if reset: + if self._converter_chains_in_reset: + self.log.debug('Converters are already in reset. ' + 'The reset bit will NOT be toggled.') + return + # Reset the ADC and DAC chains + self.log.trace('Resetting ADC chain') + self.poke(self.RF_RESET_CONTROL_OFFSET, ADC_RESET) + _wait_for_done(ADC_SEQ_DONE) + self.poke(self.RF_RESET_CONTROL_OFFSET, 0x0) + + self.log.trace('Resetting DAC chain') + self.poke(self.RF_RESET_CONTROL_OFFSET, DAC_RESET) + _wait_for_done(DAC_SEQ_DONE) + self.poke(self.RF_RESET_CONTROL_OFFSET, 0x0) + + self._converter_chains_in_reset = True + else: # enable + self._converter_chains_in_reset = False + + def log_status(self): + status = self.peek(self.RF_STATUS_OFFSET) + self.log.debug("Daughterboard 0") + self.log.debug(" @RFDC") + self.log.debug(" DAC(1:0) TREADY : {:02b}".format((status >> 0) & 0x3)) + self.log.debug(" DAC(1:0) TVALID : {:02b}".format((status >> 2) & 0x3)) + self.log.debug(" ADC(1:0) I TREADY : {:02b}".format((status >> 6) & 0x3)) + self.log.debug(" ADC(1:0) I TVALID : {:02b}".format((status >> 10) & 0x3)) + self.log.debug(" ADC(1:0) Q TREADY : {:02b}".format((status >> 4) & 0x3)) + self.log.debug(" ADC(1:0) Q TVALID : {:02b}".format((status >> 8) & 0x3)) + self.log.debug(" @USER") + self.log.debug(" ADC(1:0) OUT TVALID: {:02b}".format((status >> 12) & 0x3)) + self.log.debug(" ADC(1:0) OUT TREADY: {:02b}".format((status >> 14) & 0x3)) + self.log.debug("Daughterboard 1") + self.log.debug(" @RFDC") + self.log.debug(" DAC(1:0) TREADY : {:02b}".format((status >> 16) & 0x3)) + self.log.debug(" DAC(1:0) TVALID : {:02b}".format((status >> 18) & 0x3)) + self.log.debug(" ADC(1:0) I TREADY : {:02b}".format((status >> 22) & 0x3)) + self.log.debug(" ADC(1:0) I TVALID : {:02b}".format((status >> 26) & 0x3)) + self.log.debug(" ADC(1:0) Q TREADY : {:02b}".format((status >> 20) & 0x3)) + self.log.debug(" ADC(1:0) Q TVALID : {:02b}".format((status >> 24) & 0x3)) + self.log.debug(" @USER") + self.log.debug(" ADC(1:0) OUT TVALID: {:02b}".format((status >> 28) & 0x3)) + self.log.debug(" ADC(1:0) OUT TREADY: {:02b}".format((status >> 30) & 0x3)) + + def poke(self, addr, val): + with self.regs: + self.regs.poke32(addr, val) + + def peek(self, addr): + with self.regs: + result = self.regs.peek32(addr) + return result diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_sample_pll.py b/mpm/python/usrp_mpm/periph_manager/x4xx_sample_pll.py new file mode 100644 index 000000000..3c3040d55 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_sample_pll.py @@ -0,0 +1,315 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +LMK04832 driver for use with X4xx +""" + +from usrp_mpm.chips import LMK04832 +from usrp_mpm.sys_utils.gpio import Gpio + +class LMK04832X4xx(LMK04832): + """ + X4xx-specific subclass of the Sample Clock PLL LMK04832 controls. + """ + def __init__(self, pll_regs_iface, log=None): + LMK04832.__init__(self, pll_regs_iface, log) + self._output_freq = None + self._is_legacy_mode = None + + self._sclk_pll_reset = Gpio('SAMPLE-CLOCK-PLL-RESET', Gpio.OUTPUT, 0) + self._sclk_pll_select = Gpio('SAMPLE-CLOCK-PLL-VCXO-SELECT', Gpio.OUTPUT, 0) + + @property + def is_legacy_mode(self): + if self._is_legacy_mode is None: + self.log.error('The Sample PLL was never configured before ' + 'checking for legacy mode!') + raise RuntimeError('The Sample PLL was never configured before ' + 'checking for legacy mode!') + return self._is_legacy_mode + + @property + def output_freq(self): + if self._output_freq is None: + self.log.error('The Sample PLL was never configured before ' + 'checking the output frequency!') + raise RuntimeError('The Sample PLL was never configured before ' + 'checking the output frequency!') + return self._output_freq + + def init(self): + """ + Perform a soft reset, configure SPI readback, and verify chip ID + """ + self.reset(False, hard=True) + self.reset(True) + self.reset(False) + if not self.verify_chip_id(): + raise Exception("unable to locate LMK04832!") + + def reset(self, value=True, hard=False): + """ + Perform a hard reset from the GPIO pins or a soft reset from the LMK register + """ + if hard: + self._sclk_pll_reset.set(value) + else: + self.soft_reset(value) + + if not value: + # Enable 4-wire spi readback after a reset. 4-wire SPI is disabled + # by default after a reset of the LMK, but is required to perform + # SPI reads on the x4xx. + self.enable_4wire_spi() + + def enable_4wire_spi(self): + """ Enable 4-wire SPI readback from the CLKin_SEL0 pin """ + self.poke8(0x148, 0x33) + self.enable_3wire_spi = False + + def set_vcxo(self, source_freq): + """ + Selects either the 100e6 MHz or 122.88e6 MHz VCXO for the PLL1 loop of the LMK04832. + """ + if source_freq == 100e6: + source_index = 0 + elif source_freq == 122.88e6: + source_index = 1 + else: + self.log.warning( + 'Selected VCXO source of {:g} is not a valid selection' + .format(source_freq)) + return + self.log.trace( + 'Selected VCXO source of {:g}' + .format(source_freq)) + self._sclk_pll_select.set(source_index) + + def get_status(self): + """ + Returns PLL lock status + """ + pll1_status = self.check_plls_locked(pll='PLL1') + pll2_status = self.check_plls_locked(pll='PLL2') + return {'PLL1 lock': pll1_status, + 'PLL2 lock': pll2_status} + + def config(self, output_freq, brc_freq, is_legacy_mode=False): + """ + Configures the LMK04832 to generate the desired output_freq + """ + def calculate_vcxo_freq(output_freq): + """ + Returns the vcxo frequency based on the desired output frequency + """ + return {2.94912e9: 122.88e6, 3e9: 100e6, 3.072e9: 122.88e6}[output_freq] + def calculate_pll1_n_div(output_freq): + """ + Returns the PLL1 N divider value based on the desired output frequency + """ + return {2.94912e9: 64, 3e9: 50, 3.072e9: 64}[output_freq] + def calculate_pll2_n_div(output_freq): + """ + Returns the PLL2 N divider value based on the desired output frequency + """ + return {2.94912e9: 12, 3e9: 10, 3.072e9: 5}[output_freq] + def calculate_pll2_pre(output_freq): + """ + Returns the PLL2 prescaler value based on the desired output frequency + """ + return {2.94912e9: 2, 3e9: 3, 3.072e9: 5}[output_freq] + def calculate_n_cal_div(output_freq): + """ + Returns the PLL2 N cal value based on the desired output frequency + """ + return {2.94912e9: 12, 3e9: 10, 3.072e9: 5}[output_freq] + def calculate_sysref_div(output_freq): + """ + Returns the SYSREF divider value based on the desired output frequency + """ + return {2.94912e9: 1152, 3e9: 1200, 3.072e9: 1200}[output_freq] + def calculate_clk_in_0_r_div(output_freq, brc_freq): + """ + Returns the CLKin0 R divider value based on the desired output frequency + and current base reference clock frequency + """ + pfd1 = {2.94912e9: 40e3, 3e9: 50e3, 3.072e9: 40e3}[output_freq] + return int(brc_freq / pfd1) + + if output_freq not in (2.94912e9, 3e9, 3.072e9): + # A failure to config the SPLL could lead to an invalid state for + # downstream clocks, so throw here to alert the caller. + raise RuntimeError( + 'Selected output_freq of {:g} is not a valid selection' + .format(output_freq)) + + self._is_legacy_mode = is_legacy_mode + self._output_freq = output_freq + + self.log.trace( + f"Configuring SPLL to output frequency of {output_freq} Hz, used " + f"BRC frquency is {brc_freq} Hz, legacy mode is {is_legacy_mode}") + + self.set_vcxo(calculate_vcxo_freq(output_freq)) + + # Clear hard reset and trigger soft reset + self.reset(False, hard=True) + self.reset(True, hard=False) + self.reset(False, hard=False) + + prc_divider = 0x3C if is_legacy_mode else 0x30 + + # CLKout Config + self.pokes8(( + (0x0100, 0x01), + (0x0101, 0x0A), + (0x0102, 0x70), + (0x0103, 0x44), + (0x0104, 0x10), + (0x0105, 0x00), + (0x0106, 0x00), + (0x0107, 0x55), + (0x0108, 0x01), + (0x0109, 0x0A), + (0x010A, 0x70), + (0x010B, 0x44), + (0x010C, 0x10), + (0x010D, 0x00), + (0x010E, 0x00), + (0x010F, 0x55), + (0x0110, prc_divider), + (0x0111, 0x0A), + (0x0112, 0x60), + (0x0113, 0x40), + (0x0114, 0x10), + (0x0115, 0x00), + (0x0116, 0x00), + (0x0117, 0x44), + (0x0118, prc_divider), + (0x0119, 0x0A), + (0x011A, 0x60), + (0x011B, 0x40), + (0x011C, 0x10), + (0x011D, 0x00), + (0x011E, 0x00), + (0x011F, 0x44), + (0x0120, prc_divider), + (0x0121, 0x0A), + (0x0122, 0x60), + (0x0123, 0x40), + (0x0124, 0x20), + (0x0125, 0x00), + (0x0126, 0x00), + (0x0127, 0x44), + (0x0128, 0x01), + (0x0129, 0x0A), + (0x012A, 0x60), + (0x012B, 0x60), + (0x012C, 0x20), + (0x012D, 0x00), + (0x012E, 0x00), + (0x012F, 0x44), + (0x0130, 0x01), + (0x0131, 0x0A), + (0x0132, 0x70), + (0x0133, 0x44), + (0x0134, 0x10), + (0x0135, 0x00), + (0x0136, 0x00), + (0x0137, 0x55), + )) + + # PLL Config + sysref_div = calculate_sysref_div(output_freq) + clk_in_0_r_div = calculate_clk_in_0_r_div(output_freq, brc_freq) + pll1_n_div = calculate_pll1_n_div(output_freq) + prescaler = self.pll2_pre_to_reg(calculate_pll2_pre(output_freq)) + pll2_n_cal_div = calculate_n_cal_div(output_freq) + pll2_n_div = calculate_pll2_n_div(output_freq) + self.pokes8(( + (0x0138, 0x20), + (0x0139, 0x00), # Set SysRef source to 'Normal SYNC' as we initially use the sync signal to synchronize dividers + (0x013A, (sysref_div & 0x1F00) >> 8), # SYSREF Divide [12:8] + (0x013B, (sysref_div & 0x00FF) >> 0), # SYSREF Divide [7:0] + (0x013C, 0x00), # set sysref delay value + (0x013D, 0x20), # shift SYSREF with respect to falling edge of data clock + (0x013E, 0x03), # set number of SYSREF pulse to 8(Default) + (0x013F, 0x0F), # PLL1_NCLK_MUX = Feedback mux, FB_MUX = External, FB_MUX_EN = enabled + (0x0140, 0x00), # All power down controls set to false. + (0x0141, 0x00), # Disable dynamic digital delay. + (0x0142, 0x00), # Set dynamic digtial delay step count to 0. + (0x0143, 0x81), # Enable SYNC pin, disable sync functionality, SYSREF_CLR='0, SYNC is level sensitive. + (0x0144, 0x00), # Allow SYNC to synchronize all SysRef and clock outputs + (0x0145, 0x10), # Disable PLL1 R divider SYNC, use SYNC pin for PLL1 R divider SYNC, disable PLL2 R divider SYNC + (0x0146, 0x00), # CLKIN0/1 type = Bipolar, disable CLKin_sel pin, disable both CLKIn source for auto-switching. + (0x0147, 0x06), # ClkIn0_Demux= PLL1, CLKIn1-Demux=Feedback mux (need for 0-delay mode) + (0x0148, 0x33), # CLKIn_Sel0 = SPI readback with output set to push-pull + (0x0149, 0x02), # Set SPI readback ouput to open drain (needed for 4-wire) + (0x014A, 0x00), # Set RESET pin as input + (0x014B, 0x02), # Default + (0x014C, 0x00), # Default + (0x014D, 0x00), # Default + (0x014E, 0xC0), # Default + (0x014F, 0x7F), # Default + (0x0150, 0x00), # Default and disable holdover + (0x0151, 0x02), # Default + (0x0152, 0x00), # Default + (0x0153, (clk_in_0_r_div & 0x3F00) >> 8), # CLKin0_R divider [13:8], default = 0 + (0x0154, (clk_in_0_r_div & 0x00FF) >> 0), # CLKin0_R divider [7:0], default = d120 + (0x0155, 0x00), # Set CLKin1 R divider to 1 + (0x0156, 0x01), # Set CLKin1 R divider to 1 + (0x0157, 0x00), # Set CLKin2 R divider to 1 + (0x0158, 0x01), # Set CLKin2 R divider to 1 + (0x0159, (pll1_n_div & 0x3F00) >> 8), # PLL1 N divider [13:8], default = 0 + (0x015A, (pll1_n_div & 0x00FF) >> 0), # PLL1 N divider [7:0], default = d120 + (0x015B, 0xCF), # Set PLL1 window size to 43ns, PLL1 CP ON, negative polarity, CP gain is 1.55 mA. + (0x015C, 0x20), # Pll1 lock detect count is 8192 cycles (default) + (0x015D, 0x00), # Pll1 lock detect count is 8192 cycles (default) + (0x015E, 0x1E), # Default holdover relative time between PLL1 R and PLL1 N divider + (0x015F, 0x1B), # PLL1 and PLL2 locked status in Status_LD1 pin. Status_LD1 pin is ouput (push-pull) + (0x0160, 0x00), # PLL2 R divider is 1 + (0x0161, 0x01), # PLL2 R divider is 1 + (0x0162, prescaler), # PLL2 prescaler; OSCin freq; Lower nibble must be 0x4!!! + (0x0163, (pll2_n_cal_div & 0x030000) >> 16), # PLL2 N Cal [17:16] + (0x0164, (pll2_n_cal_div & 0x00FF00) >> 8), # PLL2 N Cal [15:8] + (0x0165, (pll2_n_cal_div & 0x0000FF) >> 0), # PLL2 N Cal [7:0] + (0x0169, 0x59), # Write this val after x165. PLL2 CP gain is 3.2 mA, PLL2 window is 1.8 ns + (0x016A, 0x20), # PLL2 lock detect count is 8192 cycles (default) + (0x016B, 0x00), # PLL2 lock detect count is 8192 cycles (default) + (0x016E, 0x13), # Stautus_LD2 pin not used. Don't care about this register + (0x0173, 0x10), # PLL2 prescaler and PLL2 are enabled. + (0x0177, 0x00), # PLL1 R divider not in reset + (0x0166, (pll2_n_div & 0x030000) >> 16), # PLL2 N[17:16] + (0x0167, (pll2_n_div & 0x00FF00) >> 8), # PLL2 N[15:8] + (0x0168, (pll2_n_div & 0x0000FF) >> 0), # PLL2 N[7:0] + )) + + # Synchronize Output and SYSREF Dividers + self.pokes8(( + (0x0143, 0x91), + (0x0143, 0xB1), + (0x0143, 0x91), + (0x0144, 0xFF), + (0x0143, 0x11), + (0x0139, 0x12), + (0x0143, 0x31), + )) + + # Check for Lock + # PLL2 should lock first and be relatively fast (300 us) + if self.wait_for_pll_lock('PLL2', timeout=5): + self.log.trace("PLL2 is locked after SPLL config.") + else: + self.log.error('Sample Clock PLL2 failed to lock!') + raise RuntimeError('Sample Clock PLL2 failed to lock! ' + 'Check the logs for details.') + # PLL1 may take up to 2 seconds to lock + if self.wait_for_pll_lock('PLL1', timeout=2000): + self.log.trace("PLL1 is locked after SPLL config.") + else: + self.log.error('Sample Clock PLL1 failed to lock!') + raise RuntimeError('Sample Clock PLL1 failed to lock! ' + 'Check the logs for details.') diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_update_cpld.py b/mpm/python/usrp_mpm/periph_manager/x4xx_update_cpld.py new file mode 100644 index 000000000..8920b6941 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_update_cpld.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Update the CPLD image for the X4xx +""" + +import sys +import os +import argparse +import subprocess +import pyudev +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.sys_utils.sysfs_gpio import GPIOBank +from usrp_mpm.sys_utils.udev import dt_symbol_get_spidev +from usrp_mpm.periph_manager.x4xx_mb_cpld import MboardCPLD +from usrp_mpm.periph_manager.x4xx import x4xx +from usrp_mpm.chips.max10_cpld_flash_ctrl import Max10CpldFlashCtrl + +OPENOCD_DIR = "/usr/share/openocd/scripts" +CONFIGS = { + 'axi_bitq' : { + 'files' : ["fpga/altera-10m50.cfg"], + 'cmd' : ["interface axi_bitq; axi_bitq_config %u %u; adapter_khz %u", + "init; svf -tap 10m50.tap %s -progress -quiet;exit"] + } +} + +AXI_BITQ_ADAPTER_SPEED = 5000 +AXI_BITQ_BUS_CLK = 40000000 + +def check_openocd_files(files, logger=None): + """ + Check if all file required by OpenOCD exist + :param logger: logger object + """ + for ocd_file in files: + if not os.path.exists(os.path.join(OPENOCD_DIR, ocd_file)): + if logger is not None: + logger.error("Missing file %s" % os.path.join(OPENOCD_DIR, ocd_file)) + return False + return True + +def check_fpga_state(which=0): + """ + Check if the FPGA is operational + :param which: the FPGA to check + """ + logger = get_logger('update_cpld') + try: + context = pyudev.Context() + fpga_mgrs = list(context.list_devices(subsystem="fpga_manager")) + if fpga_mgrs: + state = fpga_mgrs[which].attributes.asstring('state') + logger.trace("FPGA State: {}".format(state)) + return state == "operating" + return False + except OSError as ex: + logger.error("Error while checking FPGA status: {}".format(ex)) + return False + +def find_axi_bitq_uio(): + """ + Find the AXI Bitq UIO device + """ + label = 'jtag-0' + + logger = get_logger('update_cpld') + + try: + context = pyudev.Context() + for uio in context.list_devices(subsystem="uio"): + uio_label = uio.attributes.asstring('maps/map0/name') + logger.trace("UIO label: {}, match: {} number: {}".format( + uio_label, uio_label == label, uio.sys_number)) + if uio_label == label: + return int(uio.sys_number) + return None + except OSError as ex: + logger.error("Error while looking for axi_bitq uio nodes: {}".format(ex)) + return None + +def get_gpio_controls(): + """ + Instantiates an object to control JTAG related GPIO pins + Bank 3 - Pin 0: Allows toggle of JTAG Enable and additional signals + Bank 3 - Pin 1: JTAG Enable signal to the CPLD + """ + # Bank 3 starts at pin 78 + offset = 78 + mask = 0x03 + ddr = 0x03 + return GPIOBank({'label': 'zynqmp_gpio'}, offset, mask, ddr) + +def enable_jtag_gpio(gpios, disable=False): + """ + Toggle JTAG Enable line to the CPLD + """ + CPLD_JTAG_OE_n_pin = 0 + PL_CPLD_JTAGEN_pin = 1 + if not disable: + gpios.set(CPLD_JTAG_OE_n_pin, 0) # CPLD_JTAG_OE_n is active low + gpios.set(PL_CPLD_JTAGEN_pin, 1) # PL_CPLD_JTAGEN is active high + else: + gpios.set(CPLD_JTAG_OE_n_pin, 0) # CPLD_JTAG_OE_n is active low + gpios.set(PL_CPLD_JTAGEN_pin, 0) # PL_CPLD_JTAGEN is active high + +def do_update_cpld(filename, updater_mode): + """ + Carry out update process for the CPLD + :param filename: path (on device) to the new CPLD image + :param updater_mode: the updater method to use- Either flash or legacy + :return: True on success, False otherwise + """ + assert updater_mode in ('legacy', 'flash'), \ + f"Invalid updater method {updater_mode} given" + logger = get_logger('update_cpld') + logger.info("Programming CPLD of mboard with image {} using {} mode" + .format(filename, updater_mode)) + + if not os.path.exists(filename): + logger.error("CPLD image file {} not found".format(filename)) + return False + + if updater_mode == 'legacy': + return jtag_cpld_update(filename, logger) + if updater_mode == 'flash': + cpld_spi_node = dt_symbol_get_spidev('mb_cpld') + regs = MboardCPLD(cpld_spi_node, logger) + reconfig_engine_offset = 0x40 + cpld_min_revision = 0x19100108 + flash_control = Max10CpldFlashCtrl(logger, regs, reconfig_engine_offset, cpld_min_revision) + return flash_control.update(filename) + return False + +def jtag_cpld_update(filename, logger): + """ + Update the MB CPLD via dedicated JTAG lines in the FPGA + Note: To use this update mechanism, a FPGA image with JTAG + lines must be loaded. + """ + if logger is None: + logger = get_logger('update_cpld') + + if not check_fpga_state(): + logger.error("CPLD lines are routed through fabric, " + "FPGA is not programmed, giving up") + return False + + mode = 'axi_bitq' + config = CONFIGS[mode] + + if not filename.endswith('svf'): + logger.warning('The legacy JTAG programming mechanism expects ' + '.svf files. The CPLD file being used may be incorrect.') + + if check_openocd_files(config['files'], logger=logger): + logger.trace("Found required OpenOCD files.") + else: + # check_openocd_files logs errors + return False + + uio_id = find_axi_bitq_uio() + if uio_id is None or uio_id < 0: + logger.error('Failed to find axi_bitq uio devices. '\ + 'Make sure overlays are up to date') + return False + + try: + gpios = get_gpio_controls() + except RuntimeError as ex: + logger.error('Could not open GPIO required for JTAG programming!'\ + ' {}'.format(ex)) + return False + enable_jtag_gpio(gpios) + + cmd = ["openocd", + "-c", config['cmd'][0] % (uio_id, AXI_BITQ_BUS_CLK, AXI_BITQ_ADAPTER_SPEED), + "-f", (config['files'][0]).strip(), + "-c", config['cmd'][1] % filename] + + logger.trace("Update CPLD CMD: {}".format(" ".join(cmd))) + subprocess.call(cmd) + + # Disable JTAG dual-purpose pins to CPLD after reprogramming + enable_jtag_gpio(gpios, disable=True) + + logger.trace("Done programming CPLD...") + return True + + +def main(): + """ + Go, go, go! + """ + def parse_args(): + """Parse the command-line arguments""" + parser = argparse.ArgumentParser(description='Update the CPLD image on the X4xx') + parser.add_argument("--file", help="Filename of CPLD image", + default="/lib/firmware/ni/cpld-x410.rpd") + parser.add_argument("--updater", + help="The image updater method to use, either \"legacy\" or \"flash\"", + default="flash") + parser.add_argument( + '-v', + '--verbose', + help="Increase verbosity level", + action="count", + default=1 + ) + parser.add_argument( + '-q', + '--quiet', + help="Decrease verbosity level", + action="count", + default=0 + ) + return parser.parse_args() + + args = parse_args() + + # We need to make a logger if we're running stand-alone + from usrp_mpm.mpmlog import get_main_logger + log = get_main_logger(log_default_delta=args.verbose-args.quiet) + + return do_update_cpld(args.file, args.updater) + +if __name__ == "__main__": + sys.exit(not main()) diff --git a/mpm/python/usrp_mpm/sys_utils/CMakeLists.txt b/mpm/python/usrp_mpm/sys_utils/CMakeLists.txt index 71a06faf8..f3f3f40d4 100644 --- a/mpm/python/usrp_mpm/sys_utils/CMakeLists.txt +++ b/mpm/python/usrp_mpm/sys_utils/CMakeLists.txt @@ -15,6 +15,7 @@ set(USRP_MPM_SYSUTILS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/udev.py ${CMAKE_CURRENT_SOURCE_DIR}/uio.py ${CMAKE_CURRENT_SOURCE_DIR}/watchdog.py + ${CMAKE_CURRENT_SOURCE_DIR}/ectool.py ) list(APPEND USRP_MPM_FILES ${USRP_MPM_SYSUTILS_FILES}) set(USRP_MPM_FILES ${USRP_MPM_FILES} PARENT_SCOPE) diff --git a/mpm/python/usrp_mpm/sys_utils/ectool.py b/mpm/python/usrp_mpm/sys_utils/ectool.py new file mode 100644 index 000000000..0525d604a --- /dev/null +++ b/mpm/python/usrp_mpm/sys_utils/ectool.py @@ -0,0 +1,45 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Utilities for interfacing with ectool +""" + +import subprocess + +def run_cmd(cmd): + """Run ectool utility's command named cmd.""" + cmd_str = ' '.join(['ectool', cmd]) + try: + output = subprocess.check_output( + cmd_str, + stderr=subprocess.STDOUT, + shell=True, + ) + except subprocess.CalledProcessError as ex: + raise RuntimeError("Failed to execute {} ectool command".format(cmd)) + return output.decode("utf-8") + +def get_num_fans(): + """ Run ectool utility's command pwmgetnumfans to get number of fans.""" + output = run_cmd('pwmgetnumfans') + num_fans = [int(s) for s in output.split() if s.isdigit()][0] + return num_fans + +def get_fan_rpm(): + """Run ectool utility's command pwmgetfanrpm to get fan rpm.""" + num_fans = get_num_fans() + if num_fans == 0: + raise RuntimeError("Number of fans is zero.") + output = run_cmd('pwmgetfanrpm') + fan_rpm_info = [int(s) for s in output.split() if s.isdigit()] + if len(fan_rpm_info) == 2 * num_fans: + return { + "fan{}".format(fan) : fan_rpm_info[fan * 2 + 1] + for fan in range (0, num_fans) + } + else: + raise RuntimeError("Error getting fan rpm using ectool, at least one fan" \ + " may be stalled. Command output: {}".format(output)) diff --git a/mpm/python/usrp_mpm/sys_utils/gpio.py b/mpm/python/usrp_mpm/sys_utils/gpio.py index b609479f1..e864149f3 100644 --- a/mpm/python/usrp_mpm/sys_utils/gpio.py +++ b/mpm/python/usrp_mpm/sys_utils/gpio.py @@ -30,6 +30,7 @@ class Gpio: INPUT = gpiod.LINE_REQ_DIR_IN OUTPUT = gpiod.LINE_REQ_DIR_OUT + FALLING_EDGE = gpiod.LINE_REQ_EV_FALLING_EDGE def __init__(self, name, direction=INPUT, default_val=None): self._direction = direction @@ -58,3 +59,12 @@ class Gpio: with request_gpio(self._line, self._direction) as gpio: gpio.set_value(int(value)) self._out_value = bool(value) + + def event_wait(self): + """ + Wait for an event to happen on this line + """ + with request_gpio(self._line, self._direction) as gpio: + while True: + if gpio.event_wait(sec=1): + return True diff --git a/mpm/python/usrp_mpm/sys_utils/sysfs_gpio.py b/mpm/python/usrp_mpm/sys_utils/sysfs_gpio.py index b542123bc..056c28fd0 100644 --- a/mpm/python/usrp_mpm/sys_utils/sysfs_gpio.py +++ b/mpm/python/usrp_mpm/sys_utils/sysfs_gpio.py @@ -145,6 +145,7 @@ class SysFSGPIO(object): self._use_mask = use_mask self._ddr = ddr self._init_value = init_value + self._out_value = 0 self.log.trace("Generating SysFSGPIO object for identifiers `{}'..." .format(identifiers)) self._gpio_dev, self._map_info = \ @@ -182,6 +183,8 @@ class SysFSGPIO(object): open(os.path.join(GPIO_SYSFS_BASE_DIR, 'export'), 'w').write('{}'.format(gpio_num)) ddr_str = 'out' if ddr_out else 'in' ddr_str = 'high' if ini_v else ddr_str + if ini_v and ddr_out: + self._out_value |= 1 << gpio_idx self.log.trace("On GPIO path `{}', setting DDR mode to {}.".format(gpio_path, ddr_str)) open(os.path.join(GPIO_SYSFS_BASE_DIR, gpio_path, 'direction'), 'w').write(ddr_str) @@ -196,12 +199,18 @@ class SysFSGPIO(object): value = 1 assert (1<<gpio_idx) & self._use_mask assert (1<<gpio_idx) & self._ddr + assert int(value) in [0, 1] + value = int(value) gpio_num = self._base_gpio + gpio_idx gpio_path = os.path.join(GPIO_SYSFS_BASE_DIR, 'gpio{}'.format(gpio_num)) value_path = os.path.join(gpio_path, GPIO_SYSFS_VALUEFILE) self.log.trace("Writing value `{}' to `{}'...".format(value, value_path)) assert os.path.exists(value_path) open(value_path, 'w').write('{}'.format(value)) + if value: + self._out_value |= 1 << gpio_idx + else: + self._out_value &= ~(1 << gpio_idx) def reset(self, gpio_idx): """ @@ -216,19 +225,23 @@ class SysFSGPIO(object): """ Read back a GPIO at given index. - Note: The GPIO must be in the valid range, and it's DDR value must be - low (for "in"). + Note: The GPIO must be in the valid range. If it's DDR value is + low (for "in") then the register value is read from a local variable. """ assert (1<<gpio_idx) & self._use_mask - assert (1<<gpio_idx) & (~self._ddr) - gpio_num = self._base_gpio + gpio_idx - gpio_path = os.path.join(GPIO_SYSFS_BASE_DIR, 'gpio{}'.format(gpio_num)) - value_path = os.path.join(gpio_path, GPIO_SYSFS_VALUEFILE) - assert os.path.exists(value_path) - read_value = int(open(value_path, 'r').read().strip()) - self.log.trace("Reading value {} from `{}'...".format(read_value, value_path)) + if (1<<gpio_idx) & (~self._ddr): + gpio_num = self._base_gpio + gpio_idx + gpio_path = os.path.join(GPIO_SYSFS_BASE_DIR, 'gpio{}'.format(gpio_num)) + value_path = os.path.join(gpio_path, GPIO_SYSFS_VALUEFILE) + assert os.path.exists(value_path) + read_value = int(open(value_path, 'r').read().strip()) + self.log.trace("Reading value {} from `{}'...".format(read_value, value_path)) + else: + read_value = 1 if self._out_value & (1 << gpio_idx) else 0 + self.log.trace("Reading value {} from local var".format(read_value)) return read_value + class GPIOBank: """ Usability / convenience wrapper for GPIO banks accessed by SysFSGPIO diff --git a/mpm/python/usrp_mpm/sys_utils/uio.py b/mpm/python/usrp_mpm/sys_utils/uio.py index 84e4b2b64..f660c39b3 100644 --- a/mpm/python/usrp_mpm/sys_utils/uio.py +++ b/mpm/python/usrp_mpm/sys_utils/uio.py @@ -144,15 +144,15 @@ class UIO(object): else: self.log.trace("Using UIO device by label `{0}'".format(label)) self._path, map_info = find_uio_device(label, self.log) + if self._path is None or map_info is None: + self.log.error("Could not find a UIO device for label {0}".format(label)) + raise RuntimeError("Could not find a UIO device for label {0}".format(label)) # TODO If we ever support multiple maps, check if this is correct... offset = offset or map_info['offset'] assert offset == 0 # ...and then remove this line length = length or map_info['size'] self.log.trace("UIO device is being opened read-{0}.".format( "only" if read_only else "write")) - if self._path is None: - self.log.error("Could not find a UIO device for label {0}".format(label)) - raise RuntimeError("Could not find a UIO device for label {0}".format(label)) self._read_only = read_only # Our UIO objects are managed in C++ land, which gives us more granular control over # opening and closing diff --git a/mpm/python/x4xx_bist b/mpm/python/x4xx_bist new file mode 100644 index 000000000..adda5ecb0 --- /dev/null +++ b/mpm/python/x4xx_bist @@ -0,0 +1,1048 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +X4XX Built-In Self Test (BIST) + +Will work on all derivatives of the X4xx series. +""" + +import sys +import time +import subprocess +from usrp_mpm.sys_utils.udev import dt_symbol_get_spidev +from usrp_mpm import bist + +# Timeout values are in seconds: +GPS_WARMUP_TIMEOUT = 70 # Data sheet says "about a minute" +GPS_LOCKOK_TIMEOUT = 2 # Data sheet says about 15 minutes. Because our test + # does not necessarily require GPS lock to pass, we + # reduce this value in order for the BIST to pass faster + # by default. +DEFAULT_DB_ID = 0 + +# We will import stuff as late as possible, intentionally, so let's calm down +# PyLint +# pylint: disable=import-outside-toplevel + + +def get_rfdc_config(log): + """ + Return resampling, halfband settings of current FPGA image. + """ + from usrp_mpm.periph_manager import x4xx_rfdc_regs + from usrp_mpm.periph_manager import x4xx_rfdc_ctrl + rdfc_regs_control = x4xx_rfdc_regs.RfdcRegsControl( + x4xx_rfdc_ctrl.X4xxRfdcCtrl.rfdc_regs_label, log) + return rdfc_regs_control.get_rfdc_resampling_factor(0) + + +############################################################################## +# Bist class +############################################################################## +class X4XXBIST(bist.UsrpBIST): + """ + BIST Tool for the USRP X4xx series + """ + usrp_type = "X4XX" + # This defines special tests that are really collections of other tests. + collections = { + 'standard': ["gpsdo", "rtc", "temp", "fan"], + 'extended': "*", + } + # Default FPGA image type + DEFAULT_FPGA_TYPE = 'X4_200' + lv_compat_format = { + 'ddr3': { + 'throughput': -1, + }, + 'gpsdo': { + "class": "", + "time": "", + "ept": -1, + "lat": -1, + "lon": -1, + "alt": -1, + "epx": -1, + "epy": -1, + "epv": -1, + "track": -1, + "speed": -1, + "climb": -1, + "eps": -1, + "mode": -1, + }, + 'gpio': { + 'write_patterns': [], + 'read_patterns': [], + }, + 'temp': { + "DRAM PCB": 40000, + "EC Internal": 41000, + "PMBUS-0": 42000, + "PMBUS-1": 43000, + "Power Supply PCB": 44000, + "RFSoC": 45000, + "Sample Clock PCB": 46000, + "TMP464 Internal": 47000, + }, + 'fan': { + 'fan0': -1, + 'fan1': -1, + }, + } + device_args = "type=x4xx,mgmt_addr=127.0.0.1" + + def __init__(self): + bist.UsrpBIST.__init__(self) + + def get_mb_periph_mgr(self): + """Return reference to an x4xx periph manager""" + from usrp_mpm.periph_manager.x4xx import x4xx + return x4xx + + def get_product_id(self): + """Return the mboard product ID:""" + # TODO: use correct product ID, this is just a hack + # TODO: the path to the eeprom is not self-speaking like e.g. mb_eeprom + # return bist.get_product_id_from_eeprom(valid_ids=['x410'], eeprom='mb_eeprom') + return self.get_product_id_from_eeprom( + valid_ids=['0410'], + eeprom='/sys/bus/nvmem/devices/13-00500/nvmem') + + def get_product_id_from_eeprom(self, valid_ids, eeprom): + """Return the mboard product ID + Returns something like x410... + """ + # it is just here as override because eeprom-id installed on the + # x410 now needs a parameter while on n310 it runs without + # also the path to the eeprom is not self-speaking like e.g. mb_eeprom + # last problem is that the returned id is not 'x410' but '0410' + cmd = ['eeprom-id'] + cmd.append(eeprom) + cmd = ' '.join(cmd) + output = subprocess.check_output( + cmd, + stderr=subprocess.STDOUT, + shell=True, + ).decode('utf-8') + sys.stderr.write("product_id_from_eeprom: {}".format(str(output))) + for valid_id in valid_ids: + if valid_id in output: + return 'x410' + raise AssertionError("Cannot determine product ID.: `{}'".format(output)) + + def reload_fpga(self, fpga_type, sfp0_addrs): + """ + Loads the specified fpga and checks for sfp0 address, if the sfp0 address + existed, restores the original sfp0 address + """ + from usrp_mpm.sys_utils import net + from pyroute2 import IPRoute + import errno + bist.load_fpga_image( + fpga_type, + self.device_args, + 'x410', + ) + if sfp0_addrs: + ipr = IPRoute() + dev = ipr.link_lookup(ifname='sfp0')[0] + ipr.link('set', index=dev, state='down') + try: + ipr.addr('add', index=dev, address=sfp0_addrs[0], mask=24, label='sfp0') + except Exception as ex: + # If the addr already exists then ignore the error + if ex.code == errno.EEXIST: + pass + ipr.link('set', index=dev, state='up') + + def check_fpga_type_clkaux(self): + """ + clkaux BISTs require a OSS bitfile, check type and reimage if needed + If we have an OSS bitfile, we need to return a mcr that the clkaux + lmk can lock to. (Data clock rate of 122.88MHz) + """ + from usrp_mpm.periph_manager import x4xx, x4xx_periphs + + mboard_regs_control = x4xx_periphs.MboardRegsControl( + x4xx.x4xx.mboard_regs_label, self.log) + fpga_str = mboard_regs_control.get_fpga_type() + if not fpga_str or fpga_str == 'LV': + bist.load_fpga_image( + self.DEFAULT_FPGA_TYPE, + self.device_args, + 'x410', + ) + + rfdc_resamp, halfband = get_rfdc_config(self.log) + mcr = 122880000 * (8 / rfdc_resamp) + if halfband: + mcr = mcr / 2 + return mcr + +############################################################################# +# BISTS +# All bist_* methods must return True/False success values! +############################################################################# + def bist_gpsdo(self): + """ + BIST for GPSDO + Description: Returns GPS information + External Equipment: None; Recommend attaching an antenna or providing + fake GPS information + + Return dictionary: A TPV dictionary as returned by gpsd. + See also: http://www.catb.org/gpsd/gpsd_json.html + + Check for mode 2 or 3 to see if it's locked. + """ + assert 'gpsdo' in self.tests_to_run + if self.args.dry_run: + return True, { + "class": "TPV", + "time": "2017-04-30T11:48:20.10Z", + "ept": 0.005, + "lat": 30.407899, + "lon": -97.726634, + "alt": 1327.689, + "epx": 15.319, + "epy": 17.054, + "epv": 124.484, + "track": 10.3797, + "speed": 0.091, + "climb": -0.085, + "eps": 34.11, + "mode": 3 + } + from usrp_mpm.periph_manager import x4xx + # Turn on GPS, give some time to acclimatize + clk_aux_brd = x4xx.ClockingAuxBrdControl(default_source="gpsdo") + time.sleep(5) + gps_warmup_timeout = float( + self.args.option.get('gps_warmup_timeout', GPS_WARMUP_TIMEOUT)) + gps_lockok_timeout = float( + self.args.option.get('gps_lockok_timeout', GPS_LOCKOK_TIMEOUT)) + # Wait for WARMUP to go low + sys.stderr.write( + "Waiting for WARMUP to go low for up to {} seconds...\n".format( + gps_warmup_timeout)) + if not bist.poll_with_timeout( + lambda: not clk_aux_brd.get_gps_warmup(), + gps_warmup_timeout*1000, 1000 + ): + raise RuntimeError( + "GPS-WARMUP did not go low within {} seconds!".format( + gps_warmup_timeout)) + sys.stderr.write("Chip is warmed up.\n") + # Wait for LOCKOK. Data sheet says wait up to 15 minutes for GPS lock. + sys.stderr.write( + "Waiting for LOCKOK to go high for up to {} seconds...\n".format( + gps_lockok_timeout)) + if not bist.poll_with_timeout( + clk_aux_brd.get_gps_lock, + gps_lockok_timeout*1000, + 1000 + ): + sys.stderr.write("No GPS-LOCKOK!\n") + sys.stderr.write("GPS-SURVEY status: {}\n".format( + clk_aux_brd.get_gps_survey() + )) + sys.stderr.write("GPS-PHASELOCK status: {}\n".format( + clk_aux_brd.get_gps_phase_lock() + )) + sys.stderr.write("GPS-ALARM status: {}\n".format( + clk_aux_brd.get_gps_alarm() + )) + # Now the chip is on, read back the TPV result + result = bist.get_gpsd_tpv_result() + # If we reach this line, we have a valid result and the chip responded. + # However, it doesn't necessarily mean we had a GPS lock. + return True, result + + def bist_ref_clock_mboard(self): + """ + BIST for clock lock from mboard clock source (local to the motherboard) + + Description: Checks to see if the motherboard can lock to its internal + clock source. + + External Equipment: None + Return dictionary: + - <sensor-name>: + - locked: Boolean lock status + + There can be multiple ref lock sensors; for a pass condition they all + need to be asserted. + """ + assert 'ref_clock_mboard' in self.tests_to_run + if self.args.dry_run: + return True, {'ref_locked': True} + from usrp_mpm.periph_manager import x4xx_rfdc_ctrl + rfdc_resamp, fpga_halfband = get_rfdc_config(self.log) + mcrs_to_test = [] + for master_clock_rate, (_, decimation, _, halfband) in \ + x4xx_rfdc_ctrl.X4xxRfdcCtrl.master_to_sample_clk.items(): + if decimation == rfdc_resamp and fpga_halfband == halfband: + mcrs_to_test.append(master_clock_rate) + + for master_clock_rate in mcrs_to_test: + sys.stderr.write("Testing master_clock_rate {}".format(master_clock_rate)) + result = bist.get_ref_clock_prop( + 'mboard', + 'internal', + extra_args={ + 'addr': '169.254.0.2', + 'mgmt_addr': '127.0.0.1', + 'master_clock_rate': master_clock_rate, + } + ) + if 'error_msg' in result: + return False, result + return True, result + + def bist_ref_clock_ext(self): + """ + BIST for clock lock from external source. + + Description: Checks to see if the motherboard can lock to the external + reference clock. + + External Equipment: 10 MHz reference source connected to "ref in". + + Return dictionary: + - <sensor-name>: + - locked: Boolean lock status + + There can be multiple ref lock sensors; for a pass condition they all + need to be asserted. + """ + assert 'ref_clock_ext' in self.tests_to_run + if self.args.dry_run: + return True, {'ref_locked': True} + result = bist.get_ref_clock_prop( + 'external', + 'external', + extra_args={'addr': '169.254.0.2', 'mgmt_addr': '127.0.0.1'} + ) + return 'error_msg' not in result, result + + def bist_ref_clock_gpsdo(self): + """ + BIST for clock lock from gpsdo source. + + Description: Checks to see if the motherboard can lock to the gpsdo + reference clock. + + External Equipment: None + + Return dictionary: + - <sensor-name>: + - locked: Boolean lock status + + There can be multiple ref lock sensors; for a pass condition they all + need to be asserted. + """ + assert 'ref_clock_gpsdo' in self.tests_to_run + if self.args.dry_run: + return True, {'ref_locked': True} + result = bist.get_ref_clock_prop( + 'gpsdo', + 'gpsdo', + extra_args={} + ) + return 'error_msg' not in result, result + + def bist_ref_clock_int(self): + """ + BIST for clock lock from internal source. + + Description: Checks to see if the motherboard can lock to the + clocking aux board's internal reference clock. + + External Equipment: None + + Return dictionary: + - <sensor-name>: + - locked: Boolean lock status + + There can be multiple ref lock sensors; for a pass condition they all + need to be asserted. + """ + assert 'ref_clock_int' in self.tests_to_run + if self.args.dry_run: + return True, {'ref_locked': True} + result = bist.get_ref_clock_prop( + 'internal', + 'internal', + extra_args={'addr': '169.254.0.2', 'mgmt_addr': '127.0.0.1'} + ) + return 'error_msg' not in result, result + + def bist_ref_clock_nsync(self): + """ + BIST for clock lock from nsync source. + + Description: Checks to see if the motherboard can lock to the nsync + reference clock. + + External Equipment: None + + Return dictionary: + - <sensor-name>: + - locked: Boolean lock status + + There can be multiple ref lock sensors; for a pass condition they all + need to be asserted. + """ + assert 'ref_clock_nsync' in self.tests_to_run + if self.args.dry_run: + return True, {'ref_locked': True} + result = bist.get_ref_clock_prop( + 'nsync', + 'internal', + extra_args={'addr': '169.254.0.2', 'mgmt_addr': '127.0.0.1'} + ) + return 'error_msg' not in result, result + + def bist_nsync_fabric(self): + """ + BIST for testing the fabric_clk signal from the motherboard + + Description: Checks to see if the LMK on the clocking auxiliary board + can lock to the fabric clock signal output by the motherboard. We check + this by verifying that the pri_ref signal is being used, the dpll is locked, + the apll1 is locked, and apll2 is unlocked. + + External Equipment: None + + Return dictionary: + - fabric_clk pll lock: Did the clkaux lmk lock to the fabric_clk signal + """ + assert 'nsync_fabric' in self.tests_to_run + import uhd + from uhd.usrp import multi_usrp + + try: + mcr = self.check_fpga_type_clkaux() + usrp_dev = multi_usrp.MultiUSRP("type=x4xx,addr=localhost,clock_source=nsync," + "master_clock_rate={}".format(str(mcr))) + except Exception as ex: + return False, { + 'error_msg': "Failed to create usrp device: {}".format(str(ex)) + } + + mpm_c = usrp_dev.get_mpm_client() + + mpm_c.nsync_change_input_source('fabric_clk') + # The lock should happen fast, but give it half a second + time.sleep(0.5) + # Status 1 checks that the lmk is configured to use the priref signal + # True = secref, False = priref + using_pri_ref = not mpm_c.clkaux_get_nsync_status1() + # Status 0 checks dpll loss of lock + dpll_lock = not mpm_c.clkaux_get_nsync_status0() + # Reg 0xD checks several APLL statuses, we need to make sure APLL1 is + # locked and APLL2 is unlocked + apll_lock = mpm_c.peek_clkaux(0xD) == '0x8' + + result = using_pri_ref and dpll_lock and apll_lock + + mpm_c.enable_ecpri_clocks(False) + + return result, {"pri_ref_selected": using_pri_ref, + "dpll_locked": dpll_lock, + "apll1_locked_apll2_unlocked": apll_lock} + + def bist_nsync_gty(self): + """ + BIST for testing the gty_rcv_clk signal from the motherboard + + Description: Checks to see if the LMK on the clocking auxiliary board + can lock to the gty_rcv clock signal output by the motherboard. We check + this by verifying that the pri_ref signal is being used, the dpll is locked, + the apll1 is locked, and apll2 is unlocked. + + External Equipment: None + + Return dictionary: + - gty_rcv_clk pll lock: Did the clkaux lmk lock to the gty_rcv_clk signal + """ + assert 'nsync_gty' in self.tests_to_run + import uhd + from uhd.usrp import multi_usrp + + try: + mcr = self.check_fpga_type_clkaux() + usrp_dev = multi_usrp.MultiUSRP("type=x4xx,addr=localhost,clock_source=nsync," + "master_clock_rate={}".format(str(mcr))) + except Exception as ex: + return False, { + 'error_msg': "Failed to create usrp device: {}".format(str(ex)) + } + + mpm_c = usrp_dev.get_mpm_client() + + mpm_c.nsync_change_input_source('gty_rcv_clk') + # The lock should happen fast, but give it half a second + time.sleep(0.5) + # Status 1 checks that the lmk is configured to use the priref signal + # True = secref, False = priref + using_pri_ref = not mpm_c.clkaux_get_nsync_status1() + # Status 0 checks dpll loss of lock + dpll_lock = not mpm_c.clkaux_get_nsync_status0() + # Reg 0xD checks several APLL statuses, we need to make sure APLL1 is + # locked and APLL2 is unlocked + apll_lock = mpm_c.peek_clkaux(0xD) == '0x8' + + result = using_pri_ref and dpll_lock and apll_lock + + mpm_c.enable_ecpri_clocks(False) + # Set the clock source back to internal, as the register values + # for locking to the gty_rcv_clk cause the mboard to lose ref_lock + # to the nsync lmk. + mpm_c.set_clock_source('internal') + + return result, {"pri_ref_selected": using_pri_ref, + "dpll_locked": dpll_lock, + "apll1_locked_apll2_unlocked": apll_lock} + + def bist_clkaux_fpga_aux_ref(self): + """ + BIST for testing the fpga_aux_ref pps source + + Description: Checks to see if the fpga_aux_ref can output a valid pps signal + + External Equipment: None + + Return dictionary: + """ + assert 'clkaux_fpga_aux_ref' in self.tests_to_run + import uhd + from uhd.usrp import multi_usrp + + try: + mcr = self.check_fpga_type_clkaux() + usrp_dev = multi_usrp.MultiUSRP("type=x4xx,addr=localhost,clock_source=nsync," + "master_clock_rate={}".format(str(mcr))) + except Exception as ex: + return False, { + 'error_msg': "Failed to create usrp device: {}".format(str(ex)) + } + + mpm_c = usrp_dev.get_mpm_client() + + count = mpm_c.get_fpga_aux_ref_freq() + + # We expect a pps signal, pulse is reported in 40 MHz clock ticks, so + # 1 PPS is expected to return 40 million ticks, this gives 1% tolerance + return 39600000 < count < 40400000, {} + + def bist_nsync_rpll_config(self): + """ + BIST for testing that the LMK28PRIRefClk can be used as a source for the + motherboard RPLL + + Description: Enable the LMK on the clocking auxiliary board to output a signal + on the LMK28PRIRefClk line to the motherboard RPLL. Configure the RPLL to use + this source, and check to see if the RPLL can lock to the source. + + External Equipment: None + + Return dictionary: + """ + assert 'nsync_rpll_config' in self.tests_to_run + import uhd + from uhd.usrp import multi_usrp + from usrp_mpm.sys_utils import net + + try: + mcr = self.check_fpga_type_clkaux() + usrp_dev = multi_usrp.MultiUSRP("type=x4xx,addr=localhost,clock_source=nsync," + "master_clock_rate={}".format(str(mcr))) + except Exception as ex: + return False, { + 'error_msg': "Failed to create usrp device: {}".format(str(ex)) + } + + sfp0_addrs = net.get_iface_info('sfp0')['ip_addrs'] + + mpm_c = usrp_dev.get_mpm_client() + + mpm_c.config_rpll_to_nsync() + + ref_locked = mpm_c.get_ref_lock_sensor() + + result = ref_locked.get('value') == 'true' + + fpga_type = mpm_c.get_device_info()['fpga'] + + del usrp_dev + + # This is a brute-force way of making sure the device gets back into a clean state after + # we touched the rpll configuration + self.reload_fpga(fpga_type, sfp0_addrs) + + return result, ref_locked + + def bist_gpio(self): + """ + BIST for GPIO + Description: Writes and reads the values to the GPIO + + Needed Equipment: External loopback cable between port 0 and port 1 + + Notes: + - X410 has two FP-GPIO connectors (HDMI connectors) with 12 programmable + pins each + + Return dictionary: + - write_patterns: A list of patterns that were written + - read_patterns: A list of patterns that were read back + """ + assert 'gpio' in self.tests_to_run + if self.args.dry_run: + patterns = range(64) + return True, { + 'write_patterns': list(patterns), + 'read_patterns': list(patterns), + } + from usrp_mpm.periph_manager import x4xx, x4xx_periphs, x4xx_mb_cpld + mboard_regs_control = x4xx_periphs.MboardRegsControl( + x4xx.x4xx.mboard_regs_label, self.log) + cpld_spi_node = dt_symbol_get_spidev('mb_cpld') + cpld_control = x4xx_mb_cpld.MboardCPLD(cpld_spi_node, self.log) + gpio_diocontrol = x4xx_periphs.DioControl( + mboard_regs_control, + cpld_control, + self.log) + def _run_sub_test(inport, outport, pin_mode, voltage, pattern): + """ + Closure to run an actual test. The GPIO control object is enclosed. + + Arguments: + inport: "port" argument for DioControl, input port + outport: "port" argument for DioControl, input port + pin_mode: HDMI or DIO (see DioControl) + voltage: Valid arg for DioControl.set_voltage_level() + pattern: Bits to write to the inport, should be read back at outport + """ + gpio_diocontrol.set_port_mapping(pin_mode) + # We set all pins to be driven by the PS + # in HDMI mode not all pins can be accessed by the user + if pin_mode == "HDMI": + mask = 0xDB6D + else: + mask = 0xFFF + gpio_diocontrol.set_pin_masters(inport, mask) + gpio_diocontrol.set_pin_masters(outport, mask) + gpio_diocontrol.set_voltage_level(inport, voltage) + gpio_diocontrol.set_voltage_level(outport, voltage) + gpio_diocontrol.set_pin_directions(inport, 0x00000) + gpio_diocontrol.set_pin_directions(outport, 0xFFFFF) + gpio_diocontrol.set_pin_outputs(outport, pattern) + read_values = gpio_diocontrol.get_pin_inputs(inport) + if (pattern & mask) != read_values: + sys.stderr.write(gpio_diocontrol.status()) + return False, {'write_patterns': ["0x{:04X}".format(pattern)], + 'read_patterns': ["0x{:04X}".format(read_values)]} + return True, {'write_patterns': ["0x{:04X}".format(pattern)], + 'read_patterns': ["0x{:04X}".format(read_values)]} + # Now run tests: + for voltage in ["1V8", "2V5", "3V3"]: + for mode in ["DIO", "HDMI"]: + for pattern in [0xFFFF, 0xA5A5, 0x5A5A, 0x0000]: + sys.stderr.write("test: PortA -> PortB, {}, {}, 0x{:04X}" + .format(voltage, mode, pattern)) + status, data = _run_sub_test( + "PORTB", "PORTA", mode, voltage, pattern) + if not status: + return status, data + sys.stderr.write("test: PortB -> PortA, {}, {}, 0x{:04X}" + .format(voltage, mode, pattern)) + status, data = _run_sub_test( + "PORTA", "PORTB", mode, voltage, pattern) + if not status: + return status, data + return status, data + + + def bist_qsfp(self): + """ + BiST for QSFP status and property read out. + + Description: Tests MODSEL (write) and MODPRS (read) pin at QSFP port + and I2C communication with QSFP module. By default all + ports are tested. The user can add module to the option + argument when calling the test to select a specific + port out of [0,1]. A negative number will test all + ports (the default) + + Example: The following example will run the test on port 0: + > x4xx_bist qsfp --option module=0 + + Needed Equipment: None, for exhaustive test results the test should + be run with loopback test modules. + + Notes: The test ensures consistency of I2C communication and the + MODSEL and MODPRS pins. If MODPRS pin is active low + (module present) I2C communication must return valid values for + all QSFP properties. On the other hand when MODPRS pin is high + QSFP properties should report None as the only valid value. + The test also disables the I2C communication (unsetting MODSEL + pin) and checks that the low level I2C communication with the + module fails. The communication is reenabled after 3sec. This + window allows a tester to check whether a loopback adapter + signals the state of MODSEL correctly. + """ + + assert 'qsfp' in self.tests_to_run + if self.args.dry_run: + return True, {} + + from usrp_mpm.periph_manager import x4xx + from usrp_mpm.periph_manager.x4xx_periphs import QSFPModule + + def add_error_msg(msg): + # add error message to result map + if "error_msg" not in infos: + infos["error_msg"] = [] + infos["error_msg"].append(msg) + + def modules_to_test(): + module = self.args.option.get("module", -1) + try: + module = int(module) + except ValueError: + add_error_msg("Module '{}' is not an int".format(module)) + return None + if module < 0: + return x4xx.X400_QSFP_I2C_CONFIGS + if module not in [0, 1]: + add_error_msg("Module to test must be 0 or 1") + return None + return [x4xx.X400_QSFP_I2C_CONFIGS[module]] + + + infos = {} + modules_to_test = modules_to_test() + if not modules_to_test: + return False, infos + + result = True + + for config in modules_to_test: + qsfp = QSFPModule( + config.modprs, config.modsel, config.devsymbol, self.log) + info = {} + info["available"] = qsfp.is_available() + if info["available"]: + # if adapter is available, read out prop via I2C and + # verify they are all valid (not None) + props = [qsfp.decoded_status, + qsfp.vendor_name, + qsfp.connector_type] + for prop in props: + info[prop.__name__] = prop() + if not all(info.values()): + result = False + add_error_msg("QSFP adapter at {} is present but has " + "None property.".format(config.devsymbol)) + else: + # if adapter is not available + # reading a property *must* return None + if qsfp.connector_type() is not None: + result = False + add_error_msg("QSFP adapter at {} reports valid property " + "but is not present.".format(config.devsymbol)) + + infos[config.devsymbol] = info + + # disable i2c communication and check for failure on low level read + qsfp.enable_i2c(False) + try: + qsfp.qsfp_regs.peek8(0) + result = False + add_error_msg("Reading I2C when disabled should fail with " + "RuntimeError at {}\n".format(config.devsymbol)) + except RuntimeError: + pass + sys.stderr.write("A loopback QSFP adapter at {} should report MODSEL " + "inactive for 3 sec.".format(config.devsymbol)) + time.sleep(3) + qsfp.enable_i2c(True) + + return result, infos + + + def bist_temp(self): + """ + BIST for temperature sensors + Description: Reads the temperature sensors on the motherboards and + returns their values in mC + + Return dictionary: + - <thermal-zone-name>: temp in mC + """ + assert 'temp' in self.tests_to_run + if self.args.dry_run: + return True, {"DRAM PCB": 40000, + "EC Internal": 41000, + "PMBUS-0": 42000, + "PMBUS-1": 43000, + "Power Supply PCB": 44000, + "RFSoC": 45000, + "Sample Clock PCB": 46000, + "TMP464 Internal": 47000, + } + + result = bist.get_iio_temp_sensor_values() + + if len(result) < 1: + result['error_msg'] = "No temperature sensors found!" + + return 'error_msg' not in result, result + + def bist_fan(self): + """ + BIST for fans + Description: Reads the RPM values of the fans on the motherboard + + Return dictionary: + - <fan-name>: Fan speed in RPM + + External Equipment: None + """ + assert 'fan' in self.tests_to_run + if self.args.dry_run: + return True, {'fan0': 10000, 'fan1': 10000} + result = bist.get_ectool_fan_values() + return len(result) == 2, result + + def _db_flash_init(self, db_flash): + """ + Initialize the specified DB Flash and verify + its state + """ + db_flash.init() + if not db_flash.initialized: + raise RuntimeError() + + def _db_flash_deinit(self, db_flash): + """ + De-initialize the specified DB Flash and verify + its state + """ + db_flash.deinit() + if db_flash.initialized: + raise RuntimeError() + + def bist_spi_flash_integrity(self): + """ + BIST for SPI flash on DB + Description: Performs data integrity test on a section of + the flash memory. Stop the MPM service before running this + test using "systemctl stop usrp-hwd" command. + + External Equipment: None, but at least one daughterboard + should be installed in the X410 unit. + + Return dictionary: + + """ + assert 'spi_flash_integrity' in self.tests_to_run + if self.args.dry_run: + return True, {} + import os + from usrp_mpm.sys_utils.db_flash import DBFlash + FIXED_MEMORY_PATTERN = 'fixed' + RANDOM_MEMORY_PATTERN = 'random' + + db_id = int(self.args.option.get('db_id', DEFAULT_DB_ID)) + assert db_id in [0, 1] + + db_flash = DBFlash(db_id, log=None) + + buf_size = 100 + memory_pattern = str(self.args.option.get('memory_pattern', RANDOM_MEMORY_PATTERN)) + assert memory_pattern in (FIXED_MEMORY_PATTERN, RANDOM_MEMORY_PATTERN) + if memory_pattern == RANDOM_MEMORY_PATTERN: + buff = os.urandom(buf_size) + else: + buff = [0xA5] * buf_size + + data_valid = False + + try: + self._db_flash_init(db_flash) + except (ValueError, RuntimeError) as ex: + return False, {"error_msg": "Error while initializing flash storage: " + str(ex)} + + file_path = f'/mnt/db{db_id}_flash/test.bin' + + sys.stderr.write("Testing DB{} with {} memory pattern..".format(db_id, memory_pattern)) + with open(file_path, "wb") as f: + f.write(bytearray(buff)) + + try: + self._db_flash_deinit(db_flash) + self._db_flash_init(db_flash) + except (ValueError, RuntimeError) as ex: + return False, {"error_msg": "Error while init(deinit)ializing flash storage: " + str(ex)} + + with open(file_path, "rb") as f: + read_data = f.read() + data_valid = read_data == bytearray(buff) + os.remove(file_path) + self._db_flash_deinit(db_flash) + + return data_valid, {} + + def bist_spi_flash_speed(self): + """ + BIST for SPI flash on DB + Description: Performs read and write speed test on the SPI flash + memory on DB. Stop the MPM service before running this test + using "systemctl stop usrp-hwd" command. + + External Equipment: None, but at least one daughterboard + should be installed in the X410 unit. + + Return dictionary: + + """ + assert 'spi_flash_speed' in self.tests_to_run + if self.args.dry_run: + return True, {} + import os + import re + from usrp_mpm.sys_utils.db_flash import DBFlash + + MIN_WRITE_SPEED = 5000 #B/s + MIN_READ_SPEED = 5000 #B/s + def parse_speed(cmd_output): + mobj = re.search( + r"(.*records in\n)(.*records out\n)(.* (?P<speed>[0-9.]+) (?P<order>\S?)B\/s)", + cmd_output) + if mobj is None: + return 0 + scale = {'': 1, 'k': 1024, 'M': 1024*1024, 'G': 1024*1024*1024} + order = mobj.group('order') + if order not in scale: + raise ValueError(f"unsupported unit '{order}B/s'") + return float(mobj.group('speed')) * scale[order] + + db_id = int(self.args.option.get('db_id', DEFAULT_DB_ID)) + assert db_id in [0, 1] + + db_flash = DBFlash(db_id, log=None) + + file_path = f'/mnt/db{db_id}_flash/test.bin' + + try: + self._db_flash_init(db_flash) + except (ValueError, RuntimeError) as ex: + return False, { + "error_msg": "Error while initializing flash storage: {}".format(str(ex)) + } + + sys.stderr.write("Testing DB{}..".format(db_id)) + write_error_msg = None + sys.stderr.write("Write Speed Test:") + cmd = [ + 'dd', + 'if=/dev/zero', + 'of=' + file_path, + 'bs=512', + 'count=1000', + 'oflag=dsync' + ] + + try: + output = subprocess.check_output( + cmd, + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as ex: + output = ex.output + write_error_msg = "Error during Write Test: {}".format(output) + write_test_output = output.decode("utf-8") + sys.stderr.write(write_test_output) + + # De-init and init flash here to mitigate any effects + # caching may have on the read speed. + try: + self._db_flash_deinit(db_flash) + self._db_flash_init(db_flash) + except (ValueError, RuntimeError) as ex: + return False, { + "error_msg": "Error while init(deinit)ializing flash storage: {}".format(str(ex))} + + sys.stderr.write("Read Speed Test:") + read_error_msg = None + cmd = [ + 'dd', + 'if=' + file_path, + 'of=/dev/null', + 'bs=512', + 'count=1000', + 'oflag=dsync' + ] + + try: + output = subprocess.check_output( + cmd, + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as ex: + output = ex.output + read_error_msg = "Error during Read Test: {}".format(output) + read_test_output = output.decode("utf-8") + sys.stderr.write(read_test_output) + + # Clean up. + os.remove(file_path) + self._db_flash_deinit(db_flash) + + test_status = bool(write_error_msg is None and read_error_msg is None) + + if test_status: + write_speed = parse_speed(write_test_output) + read_speed = parse_speed(read_test_output) + + if write_speed == 0: + test_status = False + write_error_msg = "Write speed parse error" + elif write_speed < MIN_WRITE_SPEED: + test_status = False + write_error_msg = \ + "Write speed {} B/s is below minimum requirement of {} B/s".format( + write_speed, MIN_WRITE_SPEED) + + if read_speed == 0: + test_status = False + read_error_msg = "Read speed parse error" + elif read_speed < MIN_READ_SPEED: + test_status = False + read_error_msg = \ + "Read speed {} B/s is below minimum requirement of {} B/s".format( + read_speed, MIN_READ_SPEED) + + return test_status, { + "Write Test": write_test_output if write_error_msg is None else write_error_msg, + "Read Test": read_test_output if read_error_msg is None else read_error_msg + } + + +############################################################################## +# main +############################################################################## +def main(): + " Go, go, go! " + return X4XXBIST().run() + +if __name__ == '__main__': + sys.exit(not main()) diff --git a/mpm/systemd/CMakeLists.txt b/mpm/systemd/CMakeLists.txt index 8387c68cc..bc5b457a5 100644 --- a/mpm/systemd/CMakeLists.txt +++ b/mpm/systemd/CMakeLists.txt @@ -27,12 +27,32 @@ install(FILES DESTINATION ${SYSTEMD_SYSTEM_UNITDIR} ) +if(MPM_DEVICE STREQUAL "x4xx") + set(UHD_SELF_CAL_SERIES "X400") + set(UHD_SELF_CAL_ARGS "type=x4xx,addr=169.254.0.2,mgmt_addr=127.0.0.1") + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/system/usrp-adc-self-cal.service.in + ${CMAKE_CURRENT_BINARY_DIR}/system/usrp-adc-self-cal.service + ) + + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/system/usrp-adc-self-cal.service + DESTINATION ${SYSTEMD_SYSTEM_UNITDIR} + ) +endif() + # network configuration files install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/network/eth0.network ${CMAKE_CURRENT_SOURCE_DIR}/network/int0.network ${CMAKE_CURRENT_SOURCE_DIR}/network/sfp0.network + ${CMAKE_CURRENT_SOURCE_DIR}/network/sfp0_1.network + ${CMAKE_CURRENT_SOURCE_DIR}/network/sfp0_2.network + ${CMAKE_CURRENT_SOURCE_DIR}/network/sfp0_3.network ${CMAKE_CURRENT_SOURCE_DIR}/network/sfp1.network + ${CMAKE_CURRENT_SOURCE_DIR}/network/sfp1_1.network + ${CMAKE_CURRENT_SOURCE_DIR}/network/sfp1_2.network + ${CMAKE_CURRENT_SOURCE_DIR}/network/sfp1_3.network DESTINATION ${SYSTEMD_SYSTEM_UNITDIR}/../network ) diff --git a/mpm/systemd/network/sfp0_1.network b/mpm/systemd/network/sfp0_1.network new file mode 100644 index 000000000..ee4e5a12b --- /dev/null +++ b/mpm/systemd/network/sfp0_1.network @@ -0,0 +1,8 @@ +[Match] +Name=sfp0_1 + +[Network] +Address=192.168.11.2/24 + +[Link] +MTUBytes=9000 diff --git a/mpm/systemd/network/sfp0_2.network b/mpm/systemd/network/sfp0_2.network new file mode 100644 index 000000000..9cc90f5af --- /dev/null +++ b/mpm/systemd/network/sfp0_2.network @@ -0,0 +1,8 @@ +[Match] +Name=sfp0_2 + +[Network] +Address=192.168.12.2/24 + +[Link] +MTUBytes=9000 diff --git a/mpm/systemd/network/sfp0_3.network b/mpm/systemd/network/sfp0_3.network new file mode 100644 index 000000000..928e06d5c --- /dev/null +++ b/mpm/systemd/network/sfp0_3.network @@ -0,0 +1,8 @@ +[Match] +Name=sfp0_3 + +[Network] +Address=192.168.13.2/24 + +[Link] +MTUBytes=9000 diff --git a/mpm/systemd/network/sfp1_1.network b/mpm/systemd/network/sfp1_1.network new file mode 100644 index 000000000..c7b134aea --- /dev/null +++ b/mpm/systemd/network/sfp1_1.network @@ -0,0 +1,8 @@ +[Match] +Name=sfp1_1 + +[Network] +Address=192.168.21.2/24 + +[Link] +MTUBytes=9000 diff --git a/mpm/systemd/network/sfp1_2.network b/mpm/systemd/network/sfp1_2.network new file mode 100644 index 000000000..8e67d4510 --- /dev/null +++ b/mpm/systemd/network/sfp1_2.network @@ -0,0 +1,8 @@ +[Match] +Name=sfp1_2 + +[Network] +Address=192.168.22.2/24 + +[Link] +MTUBytes=9000 diff --git a/mpm/systemd/network/sfp1_3.network b/mpm/systemd/network/sfp1_3.network new file mode 100644 index 000000000..da17ba86b --- /dev/null +++ b/mpm/systemd/network/sfp1_3.network @@ -0,0 +1,8 @@ +[Match] +Name=sfp1_3 + +[Network] +Address=192.168.23.2/24 + +[Link] +MTUBytes=9000 diff --git a/mpm/systemd/system/usrp-adc-self-cal.service.in b/mpm/systemd/system/usrp-adc-self-cal.service.in new file mode 100644 index 000000000..cb09c36dc --- /dev/null +++ b/mpm/systemd/system/usrp-adc-self-cal.service.in @@ -0,0 +1,15 @@ +[Unit] +Description=Boot-time self-calibration of @UHD_SELF_CAL_SERIES@ devices +Requires=usrp-hwd.service +After=usrp-hwd.service + +After=network.target +Requires=network.target + +[Service] +ExecStart=@CMAKE_INSTALL_PREFIX@/bin/uhd_adc_self_cal --args @UHD_SELF_CAL_ARGS@ +Type=simple +User=root + +[Install] +WantedBy=multi-user.target diff --git a/mpm/systemd/udev/x4xx/70-sfp-net.rules b/mpm/systemd/udev/x4xx/70-sfp-net.rules new file mode 100644 index 000000000..926e927c6 --- /dev/null +++ b/mpm/systemd/udev/x4xx/70-sfp-net.rules @@ -0,0 +1,9 @@ +SUBSYSTEM=="net", KERNELS=="1200070000.ethernet", NAME="sfp1_3" +SUBSYSTEM=="net", KERNELS=="1200060000.ethernet", NAME="sfp1_2" +SUBSYSTEM=="net", KERNELS=="1200050000.ethernet", NAME="sfp1_1" +SUBSYSTEM=="net", KERNELS=="1200040000.ethernet", NAME="sfp1" +SUBSYSTEM=="net", KERNELS=="1200030000.ethernet", NAME="sfp0_3" +SUBSYSTEM=="net", KERNELS=="1200020000.ethernet", NAME="sfp0_2" +SUBSYSTEM=="net", KERNELS=="1200010000.ethernet", NAME="sfp0_1" +SUBSYSTEM=="net", KERNELS=="1200000000.ethernet", NAME="sfp0" +SUBSYSTEM=="net", KERNELS=="10000a4000.ethernet", NAME="int0" diff --git a/mpm/tools/check-filesystem b/mpm/tools/check-filesystem index 1ceb7b993..37a69b858 100755 --- a/mpm/tools/check-filesystem +++ b/mpm/tools/check-filesystem @@ -9,6 +9,8 @@ import sys import traceback import unittest +from usrp_mpm.mpmlog import get_main_logger +from usrp_mpm.periph_manager.x4xx_periphs import MboardRegsControl from usrp_mpm.sys_utils.filesystem_status import * from usrp_mpm.sys_utils.dtoverlay import list_overlays from usrp_mpm.sys_utils.dtoverlay import list_available_overlays @@ -40,13 +42,16 @@ class SimpleResult(unittest.TextTestResult): % (flavour, self.getDescription(test), err)) class CheckFilesystem(unittest.TestCase): + mboard_regs_control = None + def _assert_filesystem_root_is_not_set(self): if self.args.filesystem_root != '/': raise ValueError("Changing the filesystem root is not possible for this test") def __init__(self, methodName='runTest', args=None): super(CheckFilesystem, self).__init__(methodName) - self.args=args + self.args = args + self.log = get_main_logger() def test_uhd_version(self): uhd_version = get_uhd_version(filesystem_root=self.args.filesystem_root) @@ -56,6 +61,25 @@ class CheckFilesystem(unittest.TestCase): uhd_version = get_uhd_version(filesystem_root=self.args.filesystem_root) self.assertIn(self.args.uhd_githash, uhd_version) + def test_fpga_type(self): + if self.mboard_regs_control is None: + self.mboard_regs_control = MboardRegsControl("mboard-regs", self.log) + fpga_type = self.mboard_regs_control.get_fpga_type() + self.assertEqual(fpga_type, self.args.fpga_type) + + def test_fpga_version(self): + if self.mboard_regs_control is None: + self.mboard_regs_control = MboardRegsControl("mboard-regs", self.log) + compat_number = self.mboard_regs_control.get_compat_number() + fpga_version = "{}.{}".format(*compat_number) + self.assertEqual(fpga_version, self.args.fpga_version) + + def test_fpga_githash(self): + if self.mboard_regs_control is None: + self.mboard_regs_control = MboardRegsControl("mboard-regs", self.log) + fpga_githash = "{:x}.{}".format(*self.mboard_regs_control.get_git_hash()) + self.assertEqual(fpga_githash, self.args.fpga_githash) + def test_mender_artifact(self): mender_artifact = get_mender_artifact(filesystem_root=self.args.filesystem_root) self.assertEqual(mender_artifact, self.args.mender_artifact) @@ -119,6 +143,9 @@ def args_parser(argv): parser = argparse.ArgumentParser() parser.add_argument("--uhd-version", help="Check UHD version") parser.add_argument("--uhd-githash", help="Check UHD githash") + parser.add_argument("--fpga-type", help="Check FPGA type") + parser.add_argument("--fpga-version", help="Check FPGA version") + parser.add_argument("--fpga-githash", help="Check FPGA githash") parser.add_argument("--mender-artifact", help="Check mender artifact") parser.add_argument("--fs-version", help="Check filesystem version string") parser.add_argument("--opkg-status-date", help="Check package management status file date") diff --git a/mpm/tools/program_x4xx_clkaux_lmk05318.py b/mpm/tools/program_x4xx_clkaux_lmk05318.py new file mode 100644 index 000000000..69222a14e --- /dev/null +++ b/mpm/tools/program_x4xx_clkaux_lmk05318.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +""" +Script for programming lmk05318 on the clocking aux board +""" +# pylint: disable=no-member + +import time +import argparse +import re +from uhd.utils.mpmtools import MPMClient, MPM_RPC_PORT, InitMode + +DESCRIPTION_TEST = '''reads LMK register file generated by TICS Pro and writes via mpm_shell + the commands on the USRP given by the --host parameter.''' +parser = argparse.ArgumentParser(epilog=DESCRIPTION_TEST, + formatter_class=argparse.RawDescriptionHelpFormatter) +parser.add_argument('reg_file', help='LMK register config file') +parser.add_argument('--host', default="localhost", + help='USRP host to be programmed, usrp-hwd must run there') +parser.add_argument('-p', '--program_to_eeprom', action='store_true', + help='write config to eeprom, be careful, only 100 write-cycles are supported') +parser.add_argument('-v', '--verify_eeprom', action='store_true', + help='read config from eeprom and compares it to given reg_file') + +args = parser.parse_args() + +def parse_reg_file(file): + """ + Parse the register values from the provided register config file + """ + regfile_regex = re.compile(r"^R(\d*)\t(0x[0-9A-F]*)") + reg_dict = {} + for line in open(file, 'r').readlines(): + if regfile_regex.match(line): + [reg_str, reg_val] = line.strip().split('\t') + reg_val = reg_val.split('0x')[1] + reg_addr = reg_val[:4] + reg_data = reg_val[4:] + reg_dict[reg_str] = (hex(int(reg_addr, base=16)), hex(int(reg_data, base=16))) + return reg_dict + +mpm_c = MPMClient(InitMode.Claim, "localhost", MPM_RPC_PORT) + +# read the register file containing the actual values to be programmed +print("read register values from {}".format(args.reg_file)) +prog_regs = parse_reg_file(args.reg_file) + +print("start programming the LMK registers") +mpm_c.clkaux_set_nsync_lmk_power_en(1) +mpm_c.clkaux_set_nsync_tcxo_en(1) +if not mpm_c.clkaux_get_nsync_chip_id_valid(): + raise RuntimeError("chip ID of LMK is not valid, skip programming") +print('EEPROM has been programmed {} times'.format( + mpm_c.clkaux_get_nsync_lmk_eeprom_prog_cycles())) + +# now write the commands to program the LMK +for reg in prog_regs: + mpm_c.poke_clkaux(int(prog_regs[reg][0], 16), int(prog_regs[reg][1], 16)) + val = mpm_c.peek_clkaux(int(prog_regs[reg][0], 16)).lower() + if prog_regs[reg][1] != val: + print("warning, reg values are not as programmed: addr: {}, prog: {}, read: {}" + .format(prog_regs[reg][0], prog_regs[reg][1], val)) + +if args.program_to_eeprom: + print("programming register values into EEPROM") + print('EEPROM has been programmed {} times'.format( + mpm_c.clkaux_get_nsync_lmk_eeprom_prog_cycles())) + mpm_c.clkaux_write_nsync_lmk_cfg_regs_to_eeprom("register_commit") + mpm_c.clkaux_set_nsync_soft_reset(True) + mpm_c.clkaux_set_nsync_soft_reset(False) + mpm_c.clkaux_set_nsync_lmk_power_en(0) + print("programming of LMK done, chip is powered down") + +if args.verify_eeprom: + time.sleep(1) + VERIFY_OK = True + print("start verification of EEPROM, powering chip up") + mpm_c.clkaux_set_nsync_lmk_power_en(1) + mpm_c.clkaux_set_nsync_tcxo_en(1) + if not mpm_c.clkaux_get_nsync_chip_id_valid(): + raise RuntimeError("chip ID of LMK is not valid, skip verifying") + for reg in prog_regs: + val = mpm_c.peek_clkaux(int(prog_regs[reg][0], 16)).lower() + if str(prog_regs[reg][1]) != val: + VERIFY_OK = False + print("warning, reg values are not as programmed: addr: {}, prog: {}, read: {}" + .format(prog_regs[reg][0], prog_regs[reg][1], val)) + if VERIFY_OK: + print("SUCCESS: read back values are equal to programmed values") + else: + print("ERROR: EEPROM verification failed!") diff --git a/mpm/tools/tlv_eeprom/eeprom-pids.c b/mpm/tools/tlv_eeprom/eeprom-pids.c index d181133d7..b7d49f14d 100644 --- a/mpm/tools/tlv_eeprom/eeprom-pids.c +++ b/mpm/tools/tlv_eeprom/eeprom-pids.c @@ -8,10 +8,10 @@ #include <stddef.h> static struct pid_info pid_list[] = { - { 0x0410, "titanium", "X410 Motherboard", 0 }, + { 0x0410, "x410", "X410 Motherboard", 0 }, { 0x4000, NULL, "Power Aux Board", 0 }, { 0x4001, NULL, "Debug RF DB", 0 }, - { 0x4002, "zbx", "Zirconium RF DB", 0}, + { 0x4002, "zbx", "ZBX RF DB", 0}, { 0x4003, NULL, "HDMI SE DIO Aux Board", 0}, { 0x4004, NULL, "Clocking Aux Board with GPSDO", 0}, { 0x4005, NULL, "Clocking Aux Board (no GPSDO)", 0}, diff --git a/mpm/tools/x4xx_clkaux_lmk05318_regs_revB.txt b/mpm/tools/x4xx_clkaux_lmk05318_regs_revB.txt new file mode 100644 index 000000000..7a9a741d1 --- /dev/null +++ b/mpm/tools/x4xx_clkaux_lmk05318_regs_revB.txt @@ -0,0 +1,334 @@ +R0 0x000010 +R1 0x00010B +R2 0x000235 +R3 0x000311 +R4 0x000408 +R5 0x000516 +R6 0x000608 +R7 0x000700 +R8 0x000802 +R10 0x000AC8 +R11 0x000B00 +R12 0x000C3B +R13 0x000D08 +R14 0x000E00 +R15 0x000F00 +R16 0x001000 +R17 0x00111D +R18 0x0012FF +R19 0x001300 +R20 0x001400 +R21 0x001500 +R22 0x001600 +R23 0x001755 +R24 0x001855 +R25 0x001900 +R26 0x001A00 +R27 0x001B00 +R28 0x001C01 +R29 0x001D11 +R30 0x001E40 +R32 0x002044 +R35 0x002300 +R36 0x002403 +R37 0x002500 +R38 0x002600 +R39 0x002702 +R40 0x002800 +R41 0x002900 +R42 0x002A11 +R43 0x002B9B +R44 0x002C00 +R45 0x002D00 +R46 0x002E33 +R47 0x002F83 +R48 0x003050 +R49 0x003147 +R50 0x003218 +R51 0x003318 +R52 0x003400 +R53 0x0035F9 +R54 0x003618 +R55 0x003700 +R56 0x0038F9 +R57 0x003910 +R58 0x003A63 +R59 0x003B00 +R60 0x003C0F +R61 0x003D00 +R62 0x003E0F +R63 0x003F3B +R64 0x004095 +R65 0x004102 +R66 0x0042F8 +R67 0x0043FF +R68 0x004428 +R69 0x004500 +R70 0x004600 +R71 0x004700 +R72 0x00483F +R73 0x004900 +R74 0x004A00 +R75 0x004B00 +R76 0x004C00 +R77 0x004D0F +R78 0x004E00 +R79 0x004F11 +R80 0x005080 +R81 0x00510A +R82 0x005200 +R83 0x005305 +R84 0x0054DC +R85 0x005500 +R86 0x005600 +R87 0x00571E +R88 0x005884 +R89 0x005980 +R90 0x005A00 +R91 0x005B14 +R92 0x005C00 +R93 0x005D05 +R94 0x005EDC +R95 0x005F00 +R96 0x006000 +R97 0x00611E +R98 0x006284 +R99 0x006380 +R100 0x006429 +R101 0x006503 +R102 0x006611 +R103 0x00670F +R104 0x006818 +R105 0x006905 +R106 0x006A00 +R107 0x006B64 +R108 0x006C00 +R109 0x006D7D +R110 0x006E00 +R111 0x006F00 +R112 0x007000 +R113 0x007100 +R114 0x007200 +R115 0x007303 +R116 0x007401 +R117 0x007500 +R118 0x007600 +R119 0x007700 +R120 0x007800 +R121 0x007900 +R122 0x007A00 +R123 0x007B28 +R124 0x007C09 +R125 0x007DE5 +R126 0x007E35 +R127 0x007F2E +R128 0x008000 +R129 0x008106 +R130 0x008200 +R131 0x008301 +R132 0x008401 +R133 0x008577 +R134 0x008601 +R135 0x008713 +R136 0x008800 +R137 0x008900 +R138 0x008A00 +R139 0x008B03 +R140 0x008C02 +R141 0x008D00 +R142 0x008E01 +R143 0x008F01 +R144 0x009077 +R145 0x009101 +R146 0x009281 +R147 0x009320 +R149 0x00950D +R150 0x009600 +R151 0x009701 +R152 0x00980D +R153 0x009929 +R154 0x009A24 +R155 0x009B8B +R156 0x009C01 +R157 0x009D00 +R158 0x009E00 +R159 0x009F00 +R160 0x00A000 +R161 0x00A100 +R162 0x00A200 +R164 0x00A400 +R165 0x00A500 +R167 0x00A702 +R178 0x00B200 +R180 0x00B400 +R181 0x00B500 +R182 0x00B600 +R183 0x00B700 +R184 0x00B800 +R185 0x00B905 +R186 0x00BA01 +R187 0x00BB00 +R188 0x00BC00 +R189 0x00BD00 +R190 0x00BE01 +R191 0x00BF00 +R192 0x00C050 +R193 0x00C101 +R194 0x00C201 +R195 0x00C300 +R196 0x00C400 +R197 0x00C50D +R198 0x00C600 +R199 0x00C700 +R200 0x00C880 +R201 0x00C900 +R202 0x00CA00 +R203 0x00CB00 +R204 0x00CC07 +R205 0x00CD00 +R206 0x00CE00 +R207 0x00CF7A +R208 0x00D000 +R209 0x00D104 +R210 0x00D200 +R211 0x00D305 +R212 0x00D400 +R213 0x00D504 +R214 0x00D600 +R215 0x00D705 +R216 0x00D80F +R217 0x00D900 +R218 0x00DA01 +R219 0x00DB2C +R220 0x00DC00 +R221 0x00DD00 +R222 0x00DE03 +R223 0x00DF0D +R224 0x00E040 +R225 0x00E100 +R226 0x00E200 +R227 0x00E318 +R228 0x00E46A +R229 0x00E500 +R230 0x00E603 +R231 0x00E70D +R232 0x00E840 +R233 0x00E90A +R234 0x00EA07 +R235 0x00EB00 +R236 0x00EC00 +R237 0x00ED00 +R238 0x00EE00 +R239 0x00EF00 +R240 0x00F000 +R241 0x00F100 +R242 0x00F202 +R243 0x00F300 +R244 0x00F400 +R249 0x00F912 +R250 0x00FA00 +R251 0x00FB33 +R252 0x00FC29 +R253 0x00FD00 +R254 0x00FE00 +R255 0x00FF00 +R256 0x010006 +R257 0x010100 +R258 0x010200 +R259 0x01037D +R260 0x010402 +R261 0x010580 +R262 0x010600 +R263 0x010700 +R264 0x010800 +R265 0x0109F4 +R266 0x010A24 +R267 0x010BA0 +R268 0x010C0C +R269 0x010D00 +R270 0x010E03 +R271 0x010F7A +R272 0x01101F +R273 0x01111F +R274 0x01121F +R275 0x011313 +R276 0x011410 +R277 0x011513 +R278 0x011604 +R279 0x011708 +R280 0x011804 +R281 0x011902 +R282 0x011A07 +R283 0x011B02 +R284 0x011C1E +R285 0x011D1E +R286 0x011E02 +R287 0x011F6C +R288 0x012003 +R289 0x0121E7 +R290 0x012203 +R291 0x012325 +R292 0x012409 +R293 0x012501 +R294 0x012600 +R295 0x01272C +R296 0x012800 +R297 0x012906 +R298 0x012A00 +R299 0x012B01 +R300 0x012C00 +R301 0x012D17 +R302 0x012E1B +R303 0x012F00 +R304 0x013000 +R305 0x013100 +R306 0x013200 +R307 0x01331E +R308 0x013484 +R309 0x013580 +R310 0x013600 +R311 0x013700 +R312 0x013800 +R313 0x013900 +R314 0x013A00 +R315 0x013B00 +R316 0x013C00 +R317 0x013D00 +R318 0x013E00 +R319 0x013F03 +R320 0x014000 +R321 0x01410A +R322 0x014200 +R323 0x014300 +R324 0x014400 +R325 0x01459A +R326 0x014600 +R327 0x014703 +R328 0x01480F +R329 0x014949 +R330 0x014A00 +R331 0x014B14 +R332 0x014C00 +R333 0x014D00 +R334 0x014E00 +R335 0x014F9A +R336 0x015000 +R337 0x015103 +R338 0x01520F +R339 0x015349 +R340 0x015400 +R341 0x015500 +R342 0x015600 +R343 0x015700 +R344 0x015800 +R345 0x015900 +R346 0x015A02 +R347 0x015B00 +R348 0x015C00 +R349 0x015D00 +R350 0x015E00 +R351 0x015FB7 +R352 0x016000 +R357 0x016528 +R367 0x016F00 +R411 0x019B08 |