aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/lib/ic_reg_maps/CMakeLists.txt5
-rwxr-xr-xhost/lib/ic_reg_maps/gen_adf5355_regs.py4
-rwxr-xr-xhost/lib/ic_reg_maps/gen_adf5356_regs.py164
-rw-r--r--host/lib/usrp/common/CMakeLists.txt2
-rw-r--r--host/lib/usrp/common/adf5355.cpp354
-rw-r--r--host/lib/usrp/common/adf5355.hpp58
-rw-r--r--host/lib/usrp/common/adf535x.cpp17
-rw-r--r--host/lib/usrp/common/adf535x.hpp477
-rw-r--r--host/lib/usrp/dboard/db_twinrx.cpp26
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp77
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp10
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_ids.hpp16
12 files changed, 747 insertions, 463 deletions
diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt
index f8ccd9814..ad5835460 100644
--- a/host/lib/ic_reg_maps/CMakeLists.txt
+++ b/host/lib/ic_reg_maps/CMakeLists.txt
@@ -127,4 +127,9 @@ LIBUHD_PYTHON_GEN_SOURCE(
${CMAKE_CURRENT_BINARY_DIR}/adf5355_regs.hpp
)
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf5356_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/adf5356_regs.hpp
+)
+
SET(LIBUHD_PYTHON_GEN_SOURCE_DEPS)
diff --git a/host/lib/ic_reg_maps/gen_adf5355_regs.py b/host/lib/ic_reg_maps/gen_adf5355_regs.py
index a69f126cc..6a4f5d8a7 100755
--- a/host/lib/ic_reg_maps/gen_adf5355_regs.py
+++ b/host/lib/ic_reg_maps/gen_adf5355_regs.py
@@ -56,7 +56,7 @@ pd_polarity 4[7] 1 negative, positive
mux_logic 4[8] 1 1_8V, 3_3V
ref_mode 4[9] 0 single, diff
<% current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16))) %>\
-charge_pump_current 4[10:13] 0 ${current_setting_enums}
+charge_pump_current 4[10:13] 2 ${current_setting_enums}
double_buff_div 4[14] 0 disabled, enabled
r_counter_10_bit 4[15:24] 0
reference_divide_by_2 4[25] 0 disabled, enabled
@@ -76,7 +76,7 @@ reg6_reserved0 6[7:9] 0x0
rf_out_b_enabled 6[10] 1 enabled, disabled
mute_till_lock_detect 6[11] 0 mute_disabled, mute_enabled
reg6_reserved1 6[12] 0
-cp_bleed_current 6[13:20] 0
+cp_bleed_current 6[13:20] 2
rf_divider_select 6[21:23] 0 div1, div2, div4, div8, div16, div32, div64
feedback_select 6[24] 0 divided, fundamental
reg6_reserved2 6[25:28] 0xA
diff --git a/host/lib/ic_reg_maps/gen_adf5356_regs.py b/host/lib/ic_reg_maps/gen_adf5356_regs.py
new file mode 100755
index 000000000..d2076a807
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_adf5356_regs.py
@@ -0,0 +1,164 @@
+#!/usr/bin/env python
+#
+# Copyright 2017 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: GPL-3.0
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+REGS_TMPL="""\
+########################################################################
+## address 0
+########################################################################
+int_16_bit 0[4:19] 0
+prescaler 0[20] 0 4_5, 8_9
+autocal_en 0[21] 1 disabled, enabled
+reg0_reserved0 0[22:31] 0x000
+########################################################################
+## address 1
+########################################################################
+frac1_24_bit 1[4:27] 0
+reg1_reserved0 1[28:31] 0x0
+########################################################################
+## address 2
+########################################################################
+mod2_lsb 2[4:17] 0
+frac2_lsb 2[18:31] 0
+########################################################################
+## address 3
+########################################################################
+phase_24_bit 3[4:27] 0
+phase_adjust 3[28] 0 disabled, enabled
+phase_resync 3[29] 0 disabled, enabled
+sd_load_reset 3[30] 0 on_reg0_update, disabled
+##reserved 3[31] 0
+########################################################################
+## address 4
+########################################################################
+counter_reset 4[4] 0 disabled, enabled
+cp_three_state 4[5] 0 disabled, enabled
+power_down 4[6] 0 disabled, enabled
+pd_polarity 4[7] 1 negative, positive
+mux_logic 4[8] 1 1_8V, 3_3V
+ref_mode 4[9] 0 single, diff
+charge_pump_current 4[10:13] 0 0_30ma, 0_60ma, 0_90ma, 1_20ma, 1_50ma, 1_80ma, 2_10ma, 2_40ma, 2_70ma, 3_00ma, 3_30ma, 3_60ma, 3_90ma, 4_20ma, 4_50ma, 4_80ma
+double_buff_div 4[14] 0 disabled, enabled
+r_counter_10_bit 4[15:24] 0
+reference_divide_by_2 4[25] 0 disabled, enabled
+reference_doubler 4[26] 0 disabled, enabled
+muxout 4[27:29] 1 3state, dvdd, dgnd, rdiv, ndiv, analog_ld, dld, reserved
+reg4_reserved0 4[30:31] 0
+########################################################################
+## address 5
+########################################################################
+reg5_reserved0 5[4:31] 0x0080002
+########################################################################
+## address 6
+########################################################################
+output_power 6[4:5] 0 m4dbm, m1dbm, 2dbm, 5dbm
+rf_out_a_enabled 6[6] 0 disabled, enabled
+reg6_reserved0 6[7:9] 0x0
+rf_out_b_enabled 6[10] 1 enabled, disabled
+mute_till_lock_detect 6[11] 0 mute_disabled, mute_enabled
+reg6_reserved1 6[12] 0
+cp_bleed_current 6[13:20] 0
+rf_divider_select 6[21:23] 0 div1, div2, div4, div8, div16, div32, div64
+feedback_select 6[24] 0 divided, fundamental
+reg6_reserved2 6[25:28] 0xA
+negative_bleed 6[29] 0 disabled, enabled
+gated_bleed 6[30] 0 disabled, enabled
+bleed_polarity 6[31] 0 negative, positive
+########################################################################
+## address 7
+########################################################################
+ld_mode 7[4] 0 frac_n, int_n
+frac_n_ld_precision 7[5:6] 0 5ns, 6ns, 8ns, 12ns
+loss_of_lock_mode 7[7] 0 disabled, enabled
+ld_cyc_count 7[8:9] 0 1024, 2048, 4096, 8192
+reg7_reserved0 7[10:24] 0x0
+le_sync 7[25] 1 disabled, le_synced_to_refin
+reg7_reserved1 7[26] 1
+le_sel_sync_edge 7[27] 0 falling_edge, rising_edge
+reg7_reserved2 7[28:31] 0x0
+########################################################################
+## address 8
+########################################################################
+reg8_reserved0 8[4:31] 0x5559656
+########################################################################
+## address 9
+########################################################################
+synth_lock_timeout 9[4:8] 0
+auto_level_timeout 9[9:13] 0
+timeout 9[14:23] 0
+vco_band_div 9[24:31] 0
+########################################################################
+## address 10
+########################################################################
+adc_enable 10[4] 0 disabled, enabled
+adc_conversion 10[5] 0 disabled, enabled
+adc_clock_divider 10[6:13] 1
+reg10_reserved0 10[14:31] 0x300
+########################################################################
+## address 11
+########################################################################
+reg11_reserved0 11[4:23] 0x0061200
+vco_band_hold 11[24] 0 normal, hold
+reg11_reserved1 11[25:31] 0
+########################################################################
+## address 12
+########################################################################
+reg12_reserved0 12[4:15] 0x5f
+phase_resync_clk_div 12[16:31] 1
+########################################################################
+## address 13
+########################################################################
+mod2_msb 13[4:17] 0
+frac2_msb 13[18:31] 0
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+BODY_TMPL="""\
+enum addr_t{
+ ADDR_R0 = 0,
+ ADDR_R1 = 1,
+ ADDR_R2 = 2,
+ ADDR_R3 = 3,
+ ADDR_R4 = 4,
+ ADDR_R5 = 5,
+ ADDR_R6 = 6,
+ ADDR_R7 = 7,
+ ADDR_R8 = 8,
+ ADDR_R9 = 9,
+ ADDR_R10 = 10,
+ ADDR_R11 = 11,
+ ADDR_R12 = 12,
+ ADDR_R13 = 13
+};
+
+uint32_t get_reg(uint8_t addr){
+ uint32_t reg = addr & 0xF;
+ switch(addr){
+ % for addr in range(13+1):
+ case ${addr}:
+ % for reg in filter(lambda r: r.get_addr() == addr, regs):
+ reg |= (uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()};
+ % endfor
+ break;
+ % endfor
+ }
+ return reg;
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='adf5356_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt
index 9480d0284..d0c6daa52 100644
--- a/host/lib/usrp/common/CMakeLists.txt
+++ b/host/lib/usrp/common/CMakeLists.txt
@@ -30,7 +30,7 @@ INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver")
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/adf435x.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/adf5355.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/adf535x.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad936x_manager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver/ad9361_device.cpp
diff --git a/host/lib/usrp/common/adf5355.cpp b/host/lib/usrp/common/adf5355.cpp
deleted file mode 100644
index 36d41a5c3..000000000
--- a/host/lib/usrp/common/adf5355.cpp
+++ /dev/null
@@ -1,354 +0,0 @@
-//
-// Copyright 2013-2014 Ettus Research LLC
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#include "adf5355.hpp"
-#include "adf5355_regs.hpp"
-#include <uhd/utils/math.hpp>
-#include <boost/math/common_factor_rt.hpp> //gcd
-#include <boost/thread.hpp>
-
-using namespace uhd;
-
-template<typename data_t>
-data_t clamp(data_t val, data_t min, data_t max) {
- return (val < min) ? min : ((val > max) ? max : val);
-}
-
-template<typename data_t>
-double todbl(data_t val) {
- return static_cast<double>(val);
-}
-
-static const double ADF5355_DOUBLER_MAX_REF_FREQ = 60e6;
-//static const double ADF5355_MAX_FREQ_PFD = 125e6;
-//static const double ADF5355_PRESCALER_THRESH = 7e9;
-
-static const double ADF5355_MIN_VCO_FREQ = 3.4e9;
-//static const double ADF5355_MAX_VCO_FREQ = 6.8e9;
-static const double ADF5355_MAX_OUT_FREQ = 6.8e9;
-static const double ADF5355_MIN_OUT_FREQ = (3.4e9 / 64);
-//static const double ADF5355_MAX_OUTB_FREQ = (6.8e9 * 2);
-//static const double ADF5355_MIN_OUTB_FREQ = (3.4e9 * 2);
-
-static const double ADF5355_PHASE_RESYNC_TIME = 400e-6;
-
-static const uint32_t ADF5355_MOD1 = 16777216;
-static const uint32_t ADF5355_MAX_MOD2 = 16383;
-static const uint32_t ADF5355_MAX_FRAC2 = 16383;
-//static const uint16_t ADF5355_MIN_INT_PRESCALER_89 = 75;
-
-class adf5355_impl : public adf5355_iface
-{
-public:
- adf5355_impl(write_fn_t write_fn) :
- _write_fn(write_fn),
- _regs(),
- _rewrite_regs(true),
- _wait_time_us(0),
- _ref_freq(0.0),
- _pfd_freq(0.0),
- _fb_after_divider(false)
- {
-
- // TODO This is a hardware specific value, but can be made the default in the ic_reg_map
- _regs.charge_pump_current = adf5355_regs_t::CHARGE_PUMP_CURRENT_0_94MA;
-
- // TODO cleanup these magic numbers
- _regs.cp_bleed_current = 2;
- _regs.r_counter_10_bit = 8;
-
- _regs.vco_band_div = 3;
- _regs.timeout = 11;
- _regs.auto_level_timeout = 30;
- _regs.synth_lock_timeout = 12;
-
- _regs.adc_clock_divider = 16;
- _regs.adc_conversion = adf5355_regs_t::ADC_CONVERSION_ENABLED;
- _regs.adc_enable = adf5355_regs_t::ADC_ENABLE_ENABLED;
-
-
- // TODO Needs to be enabled for phase resync
- _regs.phase_resync = adf5355_regs_t::PHASE_RESYNC_DISABLED;
-
- // TODO Default should be divided, but there seems to be a bug preventing that. Needs rechecking
- _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
-
- // TODO 0 is an invalid value for this field. Setting to 1 seemed to break phase sync, needs retesting.
- _regs.phase_resync_clk_div = 0;
- }
-
- ~adf5355_impl()
- {
- _regs.power_down = adf5355_regs_t::POWER_DOWN_ENABLED;
- commit();
- }
-
- void set_feedback_select(feedback_sel_t fb_sel)
- {
- _fb_after_divider = (fb_sel == FB_SEL_DIVIDED);
-
- if (_fb_after_divider) {
- _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_DIVIDED;
- } else {
- _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
- }
- }
-
- void set_reference_freq(double fref, bool force = false)
- {
- //Skip the body if the reference frequency does not change
- if (uhd::math::frequencies_are_equal(fref, _ref_freq) and (not force))
- return;
-
- _ref_freq = fref;
-
- //-----------------------------------------------------------
- //Set reference settings
-
- //Reference doubler for 50% duty cycle
- bool doubler_en = (_ref_freq <= ADF5355_DOUBLER_MAX_REF_FREQ);
-
- /* Calculate and maximize PFD frequency */
- // TODO Target PFD should be configurable
- /* TwinRX requires PFD of 6.25 MHz or less */
- const double TWINRX_PFD_FREQ = 6.25e6;
- _pfd_freq = TWINRX_PFD_FREQ;
-
- int ref_div_factor = 16;
-
- //Reference divide-by-2 for 50% duty cycle
- // if R even, move one divide by 2 to to regs.reference_divide_by_2
- bool div2_en = (ref_div_factor % 2 == 0);
- if (div2_en) {
- ref_div_factor /= 2;
- }
-
- _regs.reference_divide_by_2 = div2_en ?
- adf5355_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
- adf5355_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
- _regs.reference_doubler = doubler_en ?
- adf5355_regs_t::REFERENCE_DOUBLER_ENABLED :
- adf5355_regs_t::REFERENCE_DOUBLER_DISABLED;
- _regs.r_counter_10_bit = ref_div_factor;
- UHD_ASSERT_THROW((_regs.r_counter_10_bit & ((uint16_t)~0x3FF)) == 0);
-
- //-----------------------------------------------------------
- //Set timeouts (code from ADI driver)
- _regs.timeout = clamp<uint16_t>(
- static_cast<uint16_t>(ceil(_pfd_freq / (20e3 * 30))), 1, 1023);
- UHD_ASSERT_THROW((_regs.timeout & ((uint16_t)~0x3FF)) == 0);
- _regs.synth_lock_timeout =
- static_cast<uint8_t>(ceil((_pfd_freq * 2) / (100e3 * _regs.timeout)));
- UHD_ASSERT_THROW((_regs.synth_lock_timeout & ((uint16_t)~0x1F)) == 0);
- _regs.auto_level_timeout =
- static_cast<uint8_t>(ceil((_pfd_freq * 5) / (100e3 * _regs.timeout)));
-
- //-----------------------------------------------------------
- //Set VCO band divider
- _regs.vco_band_div =
- static_cast<uint8_t>(ceil(_pfd_freq / 2.4e6));
-
- //-----------------------------------------------------------
- //Set ADC delay (code from ADI driver)
- _regs.adc_enable = adf5355_regs_t::ADC_ENABLE_ENABLED;
- _regs.adc_conversion = adf5355_regs_t::ADC_CONVERSION_ENABLED;
- _regs.adc_clock_divider = clamp<uint8_t>(
- static_cast<uint8_t>(ceil(((_pfd_freq / 100e3) - 2) / 4)), 1, 255);
- _wait_time_us = static_cast<uint32_t>(
- ceil(16e6 / (_pfd_freq / ((4 * _regs.adc_clock_divider) + 2))));
-
- //-----------------------------------------------------------
- //Phase resync
- // TODO Renable here, in initialization, or through separate set_phase_resync(bool enable) function
- _regs.phase_resync = adf5355_regs_t::PHASE_RESYNC_DISABLED;
-
- _regs.phase_adjust = adf5355_regs_t::PHASE_ADJUST_DISABLED;
- _regs.sd_load_reset = adf5355_regs_t::SD_LOAD_RESET_ON_REG0_UPDATE;
- _regs.phase_resync_clk_div = static_cast<uint16_t>(
- floor(ADF5355_PHASE_RESYNC_TIME * _pfd_freq));
-
- _rewrite_regs = true;
- }
-
- void set_output_power(output_power_t power)
- {
- adf5355_regs_t::output_power_t setting;
- switch (power) {
- case OUTPUT_POWER_M4DBM: setting = adf5355_regs_t::OUTPUT_POWER_M4DBM; break;
- case OUTPUT_POWER_M1DBM: setting = adf5355_regs_t::OUTPUT_POWER_M1DBM; break;
- case OUTPUT_POWER_2DBM: setting = adf5355_regs_t::OUTPUT_POWER_2DBM; break;
- case OUTPUT_POWER_5DBM: setting = adf5355_regs_t::OUTPUT_POWER_5DBM; break;
- default: UHD_THROW_INVALID_CODE_PATH();
- }
- if (_regs.output_power != setting) _rewrite_regs = true;
- _regs.output_power = setting;
- }
-
- void set_output_enable(output_t output, bool enable) {
-
- switch (output) {
- case RF_OUTPUT_A: _regs.rf_out_a_enabled = enable ? adf5355_regs_t::RF_OUT_A_ENABLED_ENABLED :
- adf5355_regs_t::RF_OUT_A_ENABLED_DISABLED;
- break;
- case RF_OUTPUT_B: _regs.rf_out_b_enabled = enable ? adf5355_regs_t::RF_OUT_B_ENABLED_ENABLED :
- adf5355_regs_t::RF_OUT_B_ENABLED_DISABLED;
- break;
- }
- }
-
- void set_muxout_mode(muxout_t mode)
- {
- switch (mode) {
- case MUXOUT_3STATE: _regs.muxout = adf5355_regs_t::MUXOUT_3STATE; break;
- case MUXOUT_DVDD: _regs.muxout = adf5355_regs_t::MUXOUT_DVDD; break;
- case MUXOUT_DGND: _regs.muxout = adf5355_regs_t::MUXOUT_DGND; break;
- case MUXOUT_RDIV: _regs.muxout = adf5355_regs_t::MUXOUT_RDIV; break;
- case MUXOUT_NDIV: _regs.muxout = adf5355_regs_t::MUXOUT_NDIV; break;
- case MUXOUT_ALD: _regs.muxout = adf5355_regs_t::MUXOUT_ANALOG_LD; break;
- case MUXOUT_DLD: _regs.muxout = adf5355_regs_t::MUXOUT_DLD; break;
- default: UHD_THROW_INVALID_CODE_PATH();
- }
- }
-
- double set_frequency(double target_freq, double freq_resolution, bool flush = false)
- {
- if (target_freq > ADF5355_MAX_OUT_FREQ or target_freq < ADF5355_MIN_OUT_FREQ) {
- throw uhd::runtime_error("requested frequency out of range.");
- }
- if ((uint32_t) freq_resolution == 0) {
- throw uhd::runtime_error("requested resolution cannot be less than 1.");
- }
-
- /* Calculate target VCOout frequency */
- //Increase RF divider until acceptable VCO frequency
- double target_vco_freq = target_freq;
- uint32_t rf_divider = 1;
- while (target_vco_freq < ADF5355_MIN_VCO_FREQ && rf_divider < 64) {
- target_vco_freq *= 2;
- rf_divider *= 2;
- }
-
- switch (rf_divider) {
- case 1: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV1; break;
- case 2: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV2; break;
- case 4: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV4; break;
- case 8: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV8; break;
- case 16: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV16; break;
- case 32: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV32; break;
- case 64: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV64; break;
- default: UHD_THROW_INVALID_CODE_PATH();
- }
-
- //Compute fractional PLL params
- double prescaler_input_freq = target_vco_freq;
- if (_fb_after_divider) {
- prescaler_input_freq /= rf_divider;
- }
-
- double N = prescaler_input_freq / _pfd_freq;
- uint16_t INT = static_cast<uint16_t>(floor(N));
- uint32_t FRAC1 = static_cast<uint32_t>(floor((N - INT) * ADF5355_MOD1));
- double residue = (N - INT) * ADF5355_MOD1 - FRAC1;
-
- double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution));
- uint16_t MOD2 = static_cast<uint16_t>(std::min(floor(_pfd_freq / gcd), static_cast<double>(ADF5355_MAX_MOD2)));
- uint16_t FRAC2 = static_cast<uint16_t>(std::min(ceil(residue * MOD2), static_cast<double>(ADF5355_MAX_FRAC2)));
-
- double coerced_vco_freq = _pfd_freq * (
- todbl(INT) + (
- (todbl(FRAC1) +
- (todbl(FRAC2) / todbl(MOD2)))
- / todbl(ADF5355_MOD1)
- )
- );
-
- double coerced_out_freq = coerced_vco_freq / rf_divider;
-
- /* Update registers */
- _regs.int_16_bit = INT;
- _regs.frac1_24_bit = FRAC1;
- _regs.frac2_14_bit = FRAC2;
- _regs.mod2_14_bit = MOD2;
- _regs.phase_24_bit = 0;
-
-/*
- if (_regs.int_16_bit >= ADF5355_MIN_INT_PRESCALER_89) {
- _regs.prescaler = adf5355_regs_t::PRESCALER_8_9;
- } else {
- _regs.prescaler = adf5355_regs_t::PRESCALER_4_5;
- }
-
- // ADI: Tests have shown that the optimal bleed set is the following:
- // 4/N < IBLEED/ICP < 10/N */
-/*
- uint32_t cp_curr_ua =
- (static_cast<uint32_t>(_regs.charge_pump_current) + 1) * 315;
- _regs.cp_bleed_current = clamp<uint8_t>(
- ceil((todbl(400)*cp_curr_ua) / (_regs.int_16_bit*375)), 1, 255);
- _regs.negative_bleed = adf5355_regs_t::NEGATIVE_BLEED_ENABLED;
- _regs.gated_bleed = adf5355_regs_t::GATED_BLEED_DISABLED;
-*/
-
- if (flush) commit();
- return coerced_out_freq;
- }
-
- void commit()
- {
- if (_rewrite_regs) {
- //For a full state sync write registers in reverse order 12 - 0
- addr_vtr_t regs;
- for (int addr = 12; addr >= 0; addr--) {
- regs.push_back(_regs.get_reg(uint32_t(addr)));
- }
- _write_fn(regs);
- _rewrite_regs = false;
-
- } else {
- //Frequency update sequence from data sheet
- static const size_t ONE_REG = 1;
- _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(6)));
- _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_ENABLED;
- _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4)));
- _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(2)));
- _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(1)));
- _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_DISABLED;
- _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
- _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_DISABLED;
- _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4)));
- boost::this_thread::sleep(boost::posix_time::microsec(_wait_time_us));
- _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED;
- _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
- }
- }
-
-private: //Members
- typedef std::vector<uint32_t> addr_vtr_t;
-
- write_fn_t _write_fn;
- adf5355_regs_t _regs;
- bool _rewrite_regs;
- uint32_t _wait_time_us;
- double _ref_freq;
- double _pfd_freq;
- double _fb_after_divider;
-};
-
-adf5355_iface::sptr adf5355_iface::make(write_fn_t write)
-{
- return sptr(new adf5355_impl(write));
-}
diff --git a/host/lib/usrp/common/adf5355.hpp b/host/lib/usrp/common/adf5355.hpp
deleted file mode 100644
index 55294cda1..000000000
--- a/host/lib/usrp/common/adf5355.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-// Copyright 2015 Ettus Research LLC
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#ifndef INCLUDED_ADF5355_HPP
-#define INCLUDED_ADF5355_HPP
-
-#include <boost/function.hpp>
-#include <vector>
-#include <stdint.h>
-
-class adf5355_iface
-{
-public:
- typedef boost::shared_ptr<adf5355_iface> sptr;
- typedef boost::function<void(std::vector<uint32_t>)> write_fn_t;
-
- static sptr make(write_fn_t write);
-
- virtual ~adf5355_iface() {}
-
- enum output_t { RF_OUTPUT_A, RF_OUTPUT_B };
-
- enum feedback_sel_t { FB_SEL_FUNDAMENTAL, FB_SEL_DIVIDED };
-
- enum output_power_t { OUTPUT_POWER_M4DBM, OUTPUT_POWER_M1DBM, OUTPUT_POWER_2DBM, OUTPUT_POWER_5DBM };
-
- enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD };
-
- virtual void set_reference_freq(double fref, bool force = false) = 0;
-
- virtual void set_feedback_select(feedback_sel_t fb_sel) = 0;
-
- virtual void set_output_power(output_power_t power) = 0;
-
- virtual void set_output_enable(output_t output, bool enable) = 0;
-
- virtual void set_muxout_mode(muxout_t mode) = 0;
-
- virtual double set_frequency(double target_freq, double freq_resolution, bool flush = false) = 0;
-
- virtual void commit(void) = 0;
-};
-
-#endif // INCLUDED_ADF5355_HPP
diff --git a/host/lib/usrp/common/adf535x.cpp b/host/lib/usrp/common/adf535x.cpp
new file mode 100644
index 000000000..4289d7493
--- /dev/null
+++ b/host/lib/usrp/common/adf535x.cpp
@@ -0,0 +1,17 @@
+//
+// Copyright 2013-2014, 2017 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0
+//
+
+#include "adf535x.hpp"
+
+adf535x_iface::sptr adf535x_iface::make_adf5355(write_fn_t write)
+{
+ return std::make_shared<adf535x_impl<adf5355_regs_t>>(write);
+}
+
+adf535x_iface::sptr adf535x_iface::make_adf5356(write_fn_t write)
+{
+ return std::make_shared<adf535x_impl<adf5356_regs_t>>(write);
+} \ No newline at end of file
diff --git a/host/lib/usrp/common/adf535x.hpp b/host/lib/usrp/common/adf535x.hpp
new file mode 100644
index 000000000..539290c28
--- /dev/null
+++ b/host/lib/usrp/common/adf535x.hpp
@@ -0,0 +1,477 @@
+//
+// Copyright 2015, 2017 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0
+//
+
+#ifndef INCLUDED_ADF535X_HPP
+#define INCLUDED_ADF535X_HPP
+
+#include "adf5355_regs.hpp"
+#include "adf5356_regs.hpp"
+#include <uhd/utils/math.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/function.hpp>
+#include <boost/math/common_factor_rt.hpp> //gcd
+#include <utility>
+#include <vector>
+#include <algorithm>
+#include <stdint.h>
+#include <boost/format.hpp>
+#include <uhd/utils/safe_call.hpp>
+
+class adf535x_iface
+{
+public:
+ typedef std::shared_ptr<adf535x_iface> sptr;
+ typedef std::function<void(std::vector<uint32_t>)> write_fn_t;
+
+ static sptr make_adf5355(write_fn_t write);
+ static sptr make_adf5356(write_fn_t write);
+
+ virtual ~adf535x_iface() = default;
+
+ enum output_t { RF_OUTPUT_A, RF_OUTPUT_B };
+
+ enum feedback_sel_t { FB_SEL_FUNDAMENTAL, FB_SEL_DIVIDED };
+
+ enum output_power_t { OUTPUT_POWER_M4DBM, OUTPUT_POWER_M1DBM, OUTPUT_POWER_2DBM, OUTPUT_POWER_5DBM };
+
+ enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD };
+
+ virtual void set_reference_freq(double fref, bool force = false) = 0;
+
+ virtual void set_pfd_freq(double pfd_freq) = 0;
+
+ virtual void set_feedback_select(feedback_sel_t fb_sel) = 0;
+
+ virtual void set_output_power(output_power_t power) = 0;
+
+ virtual void set_output_enable(output_t output, bool enable) = 0;
+
+ virtual void set_muxout_mode(muxout_t mode) = 0;
+
+ virtual double set_frequency(double target_freq, double freq_resolution, bool flush = false) = 0;
+
+ virtual void commit() = 0;
+};
+
+using namespace uhd;
+
+namespace {
+ const double ADF535X_DOUBLER_MAX_REF_FREQ = 60e6;
+ const double ADF535X_MAX_FREQ_PFD = 125e6;
+//const double ADF535X_PRESCALER_THRESH = 7e9;
+
+ const double ADF535X_MIN_VCO_FREQ = 3.4e9;
+//const double ADF535X_MAX_VCO_FREQ = 6.8e9;
+ const double ADF535X_MAX_OUT_FREQ = 6.8e9;
+ const double ADF535X_MIN_OUT_FREQ = (3.4e9 / 64);
+//const double ADF535X_MAX_OUTB_FREQ = (6.8e9 * 2);
+//const double ADF535X_MIN_OUTB_FREQ = (3.4e9 * 2);
+
+ const double ADF535X_PHASE_RESYNC_TIME = 400e-6;
+
+ const uint32_t ADF535X_MOD1 = 16777216;
+ const uint32_t ADF535X_MAX_MOD2 = 16383;
+ const uint32_t ADF535X_MAX_FRAC2 = 16383;
+//const uint16_t ADF535X_MIN_INT_PRESCALER_89 = 75;
+}
+
+template <typename adf535x_regs_t>
+class adf535x_impl : public adf535x_iface
+{
+public:
+ explicit adf535x_impl(write_fn_t write_fn) :
+ _write_fn(std::move(write_fn)),
+ _regs(),
+ _rewrite_regs(true),
+ _wait_time_us(0),
+ _ref_freq(0.0),
+ _pfd_freq(0.0),
+ _fb_after_divider(false)
+ {
+
+ _regs.vco_band_div = 3;
+ _regs.timeout = 11;
+ _regs.auto_level_timeout = 30;
+ _regs.synth_lock_timeout = 12;
+
+ _regs.adc_clock_divider = 16;
+ _regs.adc_conversion = adf535x_regs_t::ADC_CONVERSION_ENABLED;
+ _regs.adc_enable = adf535x_regs_t::ADC_ENABLE_ENABLED;
+
+ // TODO Needs to be enabled for phase resync
+ _regs.phase_resync = adf535x_regs_t::PHASE_RESYNC_DISABLED;
+
+ // TODO Default should be divided, but there seems to be a bug preventing that. Needs rechecking
+ _regs.feedback_select = adf535x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+
+ // TODO 0 is an invalid value for this field. Setting to 1 seemed to break phase sync, needs retesting.
+ _regs.phase_resync_clk_div = 0;
+ }
+
+ ~adf535x_impl() override
+ {
+ UHD_SAFE_CALL(
+ _regs.power_down = adf535x_regs_t::POWER_DOWN_ENABLED;
+ commit();
+ )
+ }
+
+ void set_feedback_select(const feedback_sel_t fb_sel) override
+ {
+ _fb_after_divider = (fb_sel == FB_SEL_DIVIDED);
+
+ if (_fb_after_divider) {
+ _regs.feedback_select = adf535x_regs_t::FEEDBACK_SELECT_DIVIDED;
+ } else {
+ _regs.feedback_select = adf535x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+ }
+ }
+
+ void set_pfd_freq(const double pfd_freq) override
+ {
+ if (pfd_freq > ADF535X_MAX_FREQ_PFD) {
+ UHD_LOGGER_ERROR("ADF535x") << boost::format("%f MHz is above the maximum PFD frequency of %f MHz\n")
+ % (pfd_freq/1e6) % (ADF535X_MAX_FREQ_PFD/1e6);
+ return;
+ }
+ _pfd_freq = pfd_freq;
+
+ set_reference_freq(_ref_freq);
+ }
+
+ void set_reference_freq(const double fref, const bool force = false) override
+ {
+ //Skip the body if the reference frequency does not change
+ if (uhd::math::frequencies_are_equal(fref, _ref_freq) and (not force))
+ return;
+
+ _ref_freq = fref;
+
+ //-----------------------------------------------------------
+ //Set reference settings
+ int ref_div_factor = static_cast<int>(std::floor(_ref_freq / _pfd_freq));
+
+ //Reference doubler for 50% duty cycle
+ const bool doubler_en = (_ref_freq <= ADF535X_DOUBLER_MAX_REF_FREQ);
+ if (doubler_en) {
+ ref_div_factor *= 2;
+ }
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ const bool div2_en = (ref_div_factor % 2 == 0);
+ if (div2_en) {
+ ref_div_factor /= 2;
+ }
+
+ _regs.reference_divide_by_2 = div2_en ?
+ adf535x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
+ adf535x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ _regs.reference_doubler = doubler_en ?
+ adf535x_regs_t::REFERENCE_DOUBLER_ENABLED :
+ adf535x_regs_t::REFERENCE_DOUBLER_DISABLED;
+ _regs.r_counter_10_bit = ref_div_factor;
+ UHD_ASSERT_THROW((_regs.r_counter_10_bit & ((uint16_t)~0x3FF)) == 0);
+
+ //-----------------------------------------------------------
+ //Set timeouts (code from ADI driver)
+ _regs.timeout = std::max(1, std::min(int(ceil(_pfd_freq / (20e3 * 30))), 1023));
+
+ UHD_ASSERT_THROW((_regs.timeout & ((uint16_t)~0x3FF)) == 0);
+ _regs.synth_lock_timeout =
+ static_cast<uint8_t>(ceil((_pfd_freq * 2) / (100e3 * _regs.timeout)));
+ UHD_ASSERT_THROW((_regs.synth_lock_timeout & ((uint16_t)~0x1F)) == 0);
+ _regs.auto_level_timeout =
+ static_cast<uint8_t>(ceil((_pfd_freq * 5) / (100e3 * _regs.timeout)));
+
+ //-----------------------------------------------------------
+ //Set VCO band divider
+ _regs.vco_band_div =
+ static_cast<uint8_t>(ceil(_pfd_freq / 2.4e6));
+
+ //-----------------------------------------------------------
+ //Set ADC delay (code from ADI driver)
+ _regs.adc_enable = adf535x_regs_t::ADC_ENABLE_ENABLED;
+ _regs.adc_conversion = adf535x_regs_t::ADC_CONVERSION_ENABLED;
+ _regs.adc_clock_divider = std::max(1, std::min(int(ceil(((_pfd_freq / 100e3) - 2) / 4)),255));
+
+ _wait_time_us = static_cast<uint32_t>(
+ ceil(16e6 / (_pfd_freq / ((4 * _regs.adc_clock_divider) + 2))));
+
+ //-----------------------------------------------------------
+ //Phase resync
+ // TODO Renable here, in initialization, or through separate set_phase_resync(bool enable) function
+ _regs.phase_resync = adf535x_regs_t::PHASE_RESYNC_DISABLED;
+
+ _regs.phase_adjust = adf535x_regs_t::PHASE_ADJUST_DISABLED;
+ _regs.sd_load_reset = adf535x_regs_t::SD_LOAD_RESET_ON_REG0_UPDATE;
+ _regs.phase_resync_clk_div = static_cast<uint16_t>(
+ floor(ADF535X_PHASE_RESYNC_TIME * _pfd_freq));
+
+ _rewrite_regs = true;
+ }
+
+ double set_frequency(const double target_freq, const double freq_resolution, const bool flush = false) override
+ {
+ return _set_frequency(target_freq, freq_resolution, flush);
+ }
+
+ void set_output_power(const output_power_t power) override
+ {
+ typename adf535x_regs_t::output_power_t setting;
+ switch (power) {
+ case OUTPUT_POWER_M4DBM: setting = adf535x_regs_t::OUTPUT_POWER_M4DBM; break;
+ case OUTPUT_POWER_M1DBM: setting = adf535x_regs_t::OUTPUT_POWER_M1DBM; break;
+ case OUTPUT_POWER_2DBM: setting = adf535x_regs_t::OUTPUT_POWER_2DBM; break;
+ case OUTPUT_POWER_5DBM: setting = adf535x_regs_t::OUTPUT_POWER_5DBM; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ if (_regs.output_power != setting) _rewrite_regs = true;
+ _regs.output_power = setting;
+ }
+
+ void set_output_enable(const output_t output, const bool enable) override
+ {
+ switch (output) {
+ case RF_OUTPUT_A: _regs.rf_out_a_enabled = enable ? adf535x_regs_t::RF_OUT_A_ENABLED_ENABLED :
+ adf535x_regs_t::RF_OUT_A_ENABLED_DISABLED;
+ break;
+ case RF_OUTPUT_B: _regs.rf_out_b_enabled = enable ? adf535x_regs_t::RF_OUT_B_ENABLED_ENABLED :
+ adf535x_regs_t::RF_OUT_B_ENABLED_DISABLED;
+ break;
+ }
+ }
+
+ void set_muxout_mode(const muxout_t mode) override
+ {
+ switch (mode) {
+ case MUXOUT_3STATE: _regs.muxout = adf535x_regs_t::MUXOUT_3STATE; break;
+ case MUXOUT_DVDD: _regs.muxout = adf535x_regs_t::MUXOUT_DVDD; break;
+ case MUXOUT_DGND: _regs.muxout = adf535x_regs_t::MUXOUT_DGND; break;
+ case MUXOUT_RDIV: _regs.muxout = adf535x_regs_t::MUXOUT_RDIV; break;
+ case MUXOUT_NDIV: _regs.muxout = adf535x_regs_t::MUXOUT_NDIV; break;
+ case MUXOUT_ALD: _regs.muxout = adf535x_regs_t::MUXOUT_ANALOG_LD; break;
+ case MUXOUT_DLD: _regs.muxout = adf535x_regs_t::MUXOUT_DLD; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ void commit() override
+ {
+ _commit();
+ }
+
+protected:
+ double _set_frequency(double, double, bool);
+ void _commit();
+
+private: //Members
+ typedef std::vector<uint32_t> addr_vtr_t;
+
+ write_fn_t _write_fn;
+ adf535x_regs_t _regs;
+ bool _rewrite_regs;
+ uint32_t _wait_time_us;
+ double _ref_freq;
+ double _pfd_freq;
+ double _fb_after_divider;
+};
+
+// ADF5355 Functions
+template <>
+inline double adf535x_impl<adf5355_regs_t>::_set_frequency(double target_freq, double freq_resolution, bool flush)
+{
+ if (target_freq > ADF535X_MAX_OUT_FREQ or target_freq < ADF535X_MIN_OUT_FREQ) {
+ throw uhd::runtime_error("requested frequency out of range.");
+ }
+ if ((uint32_t) freq_resolution == 0) {
+ throw uhd::runtime_error("requested resolution cannot be less than 1.");
+ }
+
+ /* Calculate target VCOout frequency */
+ //Increase RF divider until acceptable VCO frequency
+ double target_vco_freq = target_freq;
+ uint32_t rf_divider = 1;
+ while (target_vco_freq < ADF535X_MIN_VCO_FREQ && rf_divider < 64) {
+ target_vco_freq *= 2;
+ rf_divider *= 2;
+ }
+
+ switch (rf_divider) {
+ case 1: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV1; break;
+ case 2: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV2; break;
+ case 4: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV4; break;
+ case 8: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV8; break;
+ case 16: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV16; break;
+ case 32: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV32; break;
+ case 64: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV64; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ //Compute fractional PLL params
+ double prescaler_input_freq = target_vco_freq;
+ if (_fb_after_divider) {
+ prescaler_input_freq /= rf_divider;
+ }
+
+ const double N = prescaler_input_freq / _pfd_freq;
+ const auto INT = static_cast<uint16_t>(floor(N));
+ const auto FRAC1 = static_cast<uint32_t>(floor((N - INT) * ADF535X_MOD1));
+ const double residue = (N - INT) * ADF535X_MOD1 - FRAC1;
+
+ const double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution));
+ const auto MOD2 = static_cast<uint16_t>(std::min(floor(_pfd_freq / gcd), static_cast<double>(ADF535X_MAX_MOD2)));
+ const auto FRAC2 = static_cast<uint16_t>(std::min(ceil(residue * MOD2), static_cast<double>(ADF535X_MAX_FRAC2)));
+
+ const double coerced_vco_freq = _pfd_freq * (
+ double(INT) + (
+ (double(FRAC1) +
+ (double(FRAC2) / double(MOD2)))
+ / double(ADF535X_MOD1)
+ )
+ );
+
+ const double coerced_out_freq = coerced_vco_freq / rf_divider;
+
+ /* Update registers */
+ _regs.int_16_bit = INT;
+ _regs.frac1_24_bit = FRAC1;
+ _regs.frac2_14_bit = FRAC2;
+ _regs.mod2_14_bit = MOD2;
+ _regs.phase_24_bit = 0;
+
+ if (flush) commit();
+ return coerced_out_freq;
+}
+
+template <>
+inline void adf535x_impl<adf5355_regs_t>::_commit()
+{
+ const size_t ONE_REG = 1;
+
+ if (_rewrite_regs) {
+ //For a full state sync write registers in reverse order 12 - 0
+ addr_vtr_t regs;
+ for (uint8_t addr = 12; addr > 0; addr--) {
+ regs.push_back(_regs.get_reg(addr));
+ }
+ _write_fn(regs);
+ // TODO Add FPGA based delay between these writes
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ _rewrite_regs = false;
+
+ } else {
+ //Frequency update sequence from data sheet
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(6)));
+ _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_ENABLED;
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(2)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(1)));
+ _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_DISABLED;
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_DISABLED;
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4)));
+ // TODO Add FPGA based delay between these writes
+ _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED;
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ }
+}
+
+// ADF5356 Functions
+template <>
+inline double adf535x_impl<adf5356_regs_t>::_set_frequency(double target_freq, double freq_resolution, bool flush)
+{
+ if (target_freq > ADF535X_MAX_OUT_FREQ or target_freq < ADF535X_MIN_OUT_FREQ) {
+ throw uhd::runtime_error("requested frequency out of range.");
+ }
+ if ((uint32_t) freq_resolution == 0) {
+ throw uhd::runtime_error("requested resolution cannot be less than 1.");
+ }
+
+ /* Calculate target VCOout frequency */
+ //Increase RF divider until acceptable VCO frequency
+ double target_vco_freq = target_freq;
+ uint32_t rf_divider = 1;
+ while (target_vco_freq < ADF535X_MIN_VCO_FREQ && rf_divider < 64) {
+ target_vco_freq *= 2;
+ rf_divider *= 2;
+ }
+
+ switch (rf_divider) {
+ case 1: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV1; break;
+ case 2: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV2; break;
+ case 4: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV4; break;
+ case 8: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV8; break;
+ case 16: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV16; break;
+ case 32: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV32; break;
+ case 64: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV64; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ //Compute fractional PLL params
+ double prescaler_input_freq = target_vco_freq;
+ if (_fb_after_divider) {
+ prescaler_input_freq /= rf_divider;
+ }
+
+ const double N = prescaler_input_freq / _pfd_freq;
+ const auto INT = static_cast<uint16_t>(floor(N));
+ const auto FRAC1 = static_cast<uint32_t>(floor((N - INT) * ADF535X_MOD1));
+ const double residue = (N - INT) * ADF535X_MOD1 - FRAC1;
+
+ const double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution));
+ const auto MOD2 = static_cast<uint16_t>(std::min(floor(_pfd_freq / gcd), static_cast<double>(ADF535X_MAX_MOD2)));
+ const auto FRAC2 = static_cast<uint16_t>(std::min(ceil(residue * MOD2), static_cast<double>(ADF535X_MAX_FRAC2)));
+
+ const double coerced_vco_freq = _pfd_freq * (
+ double(INT) + (
+ (double(FRAC1) +
+ (double(FRAC2) / double(MOD2)))
+ / double(ADF535X_MOD1)
+ )
+ );
+
+ const double coerced_out_freq = coerced_vco_freq / rf_divider;
+
+ /* Update registers */
+ _regs.int_16_bit = INT;
+ _regs.frac1_24_bit = FRAC1;
+ _regs.frac2_msb = FRAC2;
+ _regs.mod2_msb = MOD2;
+ _regs.phase_24_bit = 0;
+
+ if (flush) commit();
+ return coerced_out_freq;
+}
+
+template <>
+inline void adf535x_impl<adf5356_regs_t>::_commit()
+{
+ const size_t ONE_REG = 1;
+ if (_rewrite_regs) {
+ //For a full state sync write registers in reverse order 12 - 0
+ addr_vtr_t regs;
+ for (uint8_t addr = 13; addr > 0; addr--) {
+ regs.push_back(_regs.get_reg(addr));
+ }
+ _write_fn(regs);
+ // TODO Add FPGA based delay between these writes
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ _rewrite_regs = false;
+
+ } else {
+ //Frequency update sequence from data sheet
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(13)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(10)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(6)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(2)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(1)));
+ // TODO Add FPGA based delay between these writes
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ }
+}
+
+#endif // INCLUDED_ADF535X_HPP
diff --git a/host/lib/usrp/dboard/db_twinrx.cpp b/host/lib/usrp/dboard/db_twinrx.cpp
index cedc26c36..254bb0344 100644
--- a/host/lib/usrp/dboard/db_twinrx.cpp
+++ b/host/lib/usrp/dboard/db_twinrx.cpp
@@ -18,6 +18,7 @@
#include "twinrx/twinrx_experts.hpp"
#include "twinrx/twinrx_ctrl.hpp"
#include "twinrx/twinrx_io.hpp"
+#include "twinrx/twinrx_ids.hpp"
#include <expert_factory.hpp>
#include <uhd/types/device_addr.hpp>
#include <uhd/types/ranges.hpp>
@@ -38,9 +39,6 @@ using namespace uhd::usrp;
using namespace uhd::usrp::dboard::twinrx;
using namespace uhd::experts;
-static const dboard_id_t TWINRX_V100_000_ID(0x91);
-static const dboard_id_t TWINRX_V100_100_ID(0x93);
-
/*!
* twinrx_rcvr_fe is the dbaord class (dboard_base) that
* represents each front-end of a TwinRX board. UHD will
@@ -227,7 +225,7 @@ public:
twinrx_gpio::sptr gpio_iface = boost::make_shared<twinrx_gpio>(_db_iface);
twinrx_cpld_regmap::sptr cpld_regs = boost::make_shared<twinrx_cpld_regmap>();
cpld_regs->initialize(*gpio_iface, false);
- _ctrl = twinrx_ctrl::make(_db_iface, gpio_iface, cpld_regs);
+ _ctrl = twinrx_ctrl::make(_db_iface, gpio_iface, cpld_regs, get_rx_id());
_expert = expert_factory::create_container("twinrx_expert");
}
@@ -335,18 +333,26 @@ static dboard_base::sptr make_twinrx_container(dboard_base::ctor_args_t args)
UHD_STATIC_BLOCK(reg_twinrx_dboards)
{
dboard_manager::register_dboard_restricted(
- TWINRX_V100_000_ID,
+ twinrx::TWINRX_REV_A_ID,
+ &twinrx_rcvr::make_twinrx_fe,
+ "TwinRX Rev A",
+ {"0", "1"},
+ &make_twinrx_container
+ );
+
+ dboard_manager::register_dboard_restricted(
+ twinrx::TWINRX_REV_B_ID,
&twinrx_rcvr::make_twinrx_fe,
- "TwinRX v1.0",
- boost::assign::list_of("0")("1"),
+ "TwinRX Rev B",
+ {"0", "1"},
&make_twinrx_container
);
dboard_manager::register_dboard_restricted(
- TWINRX_V100_100_ID,
+ twinrx::TWINRX_REV_C_ID,
&twinrx_rcvr::make_twinrx_fe,
- "TwinRX v1.1",
- boost::assign::list_of("0")("1"),
+ "TwinRX Rev C",
+ {"0", "1"},
&make_twinrx_container
);
}
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp
index 00be82e1a..1ce2f0e0e 100644
--- a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp
+++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp
@@ -1,42 +1,38 @@
//
-// Copyright 2015 Ettus Research LLC
+// Copyright 2015-2017 Ettus Research, A National Instruments Company
//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
+// SPDX-License-Identifier: GPL-3.0
//
-#include <boost/thread/thread.hpp>
-#include <uhd/utils/math.hpp>
-#include <uhd/utils/safe_call.hpp>
#include "twinrx_ctrl.hpp"
+#include "twinrx_ids.hpp"
#include "adf435x.hpp"
-#include "adf5355.hpp"
+#include "adf535x.hpp"
+#include <uhd/utils/math.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <boost/thread/thread.hpp>
using namespace uhd;
using namespace usrp;
using namespace dboard::twinrx;
-typedef twinrx_cpld_regmap rm;
-static uint32_t bool2bin(bool x) { return x ? 1 : 0; }
+namespace {
+ typedef twinrx_cpld_regmap rm;
-static const double TWINRX_DESIRED_REFERENCE_FREQ = 50e6;
+ uint32_t bool2bin(bool x) { return x ? 1 : 0; }
+
+ const double TWINRX_DESIRED_REFERENCE_FREQ = 50e6;
+ const double TWINRX_REV_AB_PFD_FREQ = 6.25e6;
+ const double TWINRX_REV_C_PFD_FREQ = 12.6e6;
+}
class twinrx_ctrl_impl : public twinrx_ctrl {
public:
twinrx_ctrl_impl(
dboard_iface::sptr db_iface,
twinrx_gpio::sptr gpio_iface,
- twinrx_cpld_regmap::sptr cpld_regmap
+ twinrx_cpld_regmap::sptr cpld_regmap,
+ const dboard_id_t rx_id
) : _db_iface(db_iface), _gpio_iface(gpio_iface), _cpld_regs(cpld_regmap)
{
@@ -50,10 +46,24 @@ public:
}
}
//Initialize synthesizer objects
- _lo1_iface[size_t(CH1)] = adf5355_iface::make(
- boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, _1));
- _lo1_iface[size_t(CH2)] = adf5355_iface::make(
- boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, _1));
+ if (rx_id == twinrx::TWINRX_REV_C_ID) {
+ _lo1_iface[size_t(CH1)] = adf535x_iface::make_adf5356(
+ std::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, std::placeholders::_1));
+ _lo1_iface[size_t(CH2)] = adf535x_iface::make_adf5356(
+ std::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, std::placeholders::_1));
+
+ _lo1_iface[size_t(CH1)]->set_pfd_freq(TWINRX_REV_C_PFD_FREQ);
+ _lo1_iface[size_t(CH2)]->set_pfd_freq(TWINRX_REV_C_PFD_FREQ);
+
+ } else {
+ _lo1_iface[size_t(CH1)] = adf535x_iface::make_adf5355(
+ std::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, std::placeholders::_1));
+ _lo1_iface[size_t(CH2)] = adf535x_iface::make_adf5355(
+ std::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, std::placeholders::_1));
+
+ _lo1_iface[size_t(CH1)]->set_pfd_freq(TWINRX_REV_AB_PFD_FREQ);
+ _lo1_iface[size_t(CH2)]->set_pfd_freq(TWINRX_REV_AB_PFD_FREQ);
+ }
_lo2_iface[size_t(CH1)] = adf435x_iface::make_adf4351(
boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_RX, _1));
@@ -104,11 +114,9 @@ public:
for (size_t i = 0; i < NUM_CHANS; i++) {
_config_lo1_route(i==0?LO_CONFIG_CH1:LO_CONFIG_CH2);
_config_lo2_route(i==0?LO_CONFIG_CH1:LO_CONFIG_CH2);
- _lo1_iface[i]->set_output_power(adf5355_iface::OUTPUT_POWER_5DBM);
+ _lo1_iface[i]->set_output_power(adf535x_iface::OUTPUT_POWER_5DBM);
_lo1_iface[i]->set_reference_freq(TWINRX_DESIRED_REFERENCE_FREQ);
- // Divided feedback did not appear to be correctly implemented during bringup. Necessary for phase resync
-// _lo1_iface[i]->set_feedback_select(adf5355_iface::FB_SEL_DIVIDED);
- _lo1_iface[i]->set_muxout_mode(adf5355_iface::MUXOUT_DLD);
+ _lo1_iface[i]->set_muxout_mode(adf535x_iface::MUXOUT_DLD);
_lo1_iface[i]->set_frequency(3e9, 1.0e3);
_lo2_iface[i]->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED);
_lo2_iface[i]->set_output_power(adf435x_iface::OUTPUT_POWER_5DBM);
@@ -535,8 +543,8 @@ private: //Functions
_lo2_src[size_t(CH1)] == LO_COMPANION ||
_lo2_export == LO_CH2_SYNTH;
- _lo1_iface[size_t(CH1)]->set_output_enable(adf5355_iface::RF_OUTPUT_A, _lo1_enable[size_t(CH1)].get());
- _lo1_iface[size_t(CH2)]->set_output_enable(adf5355_iface::RF_OUTPUT_A, _lo1_enable[size_t(CH2)].get());
+ _lo1_iface[size_t(CH1)]->set_output_enable(adf535x_iface::RF_OUTPUT_A, _lo1_enable[size_t(CH1)].get());
+ _lo1_iface[size_t(CH2)]->set_output_enable(adf535x_iface::RF_OUTPUT_A, _lo1_enable[size_t(CH2)].get());
_lo2_iface[size_t(CH1)]->set_output_enable(adf435x_iface::RF_OUTPUT_A, _lo2_enable[size_t(CH1)].get());
_lo2_iface[size_t(CH2)]->set_output_enable(adf435x_iface::RF_OUTPUT_A, _lo2_enable[size_t(CH2)].get());
@@ -624,7 +632,7 @@ private: //Members
dboard_iface::sptr _db_iface;
twinrx_gpio::sptr _gpio_iface;
twinrx_cpld_regmap::sptr _cpld_regs;
- adf5355_iface::sptr _lo1_iface[NUM_CHANS];
+ adf535x_iface::sptr _lo1_iface[NUM_CHANS];
adf435x_iface::sptr _lo2_iface[NUM_CHANS];
lo_source_t _lo1_src[NUM_CHANS];
lo_source_t _lo2_src[NUM_CHANS];
@@ -639,7 +647,8 @@ private: //Members
twinrx_ctrl::sptr twinrx_ctrl::make(
dboard_iface::sptr db_iface,
twinrx_gpio::sptr gpio_iface,
- twinrx_cpld_regmap::sptr cpld_regmap
+ twinrx_cpld_regmap::sptr cpld_regmap,
+ const dboard_id_t rx_id
) {
- return sptr(new twinrx_ctrl_impl(db_iface, gpio_iface, cpld_regmap));
+ return std::make_shared<twinrx_ctrl_impl>(db_iface, gpio_iface, cpld_regmap, rx_id);
}
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp
index 2439addc4..866a7c54d 100644
--- a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp
+++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp
@@ -18,20 +18,22 @@
#ifndef INCLUDED_DBOARD_TWINRX_CTRL_HPP
#define INCLUDED_DBOARD_TWINRX_CTRL_HPP
-#include <boost/noncopyable.hpp>
-#include <uhd/types/wb_iface.hpp>
#include "twinrx_io.hpp"
+#include <uhd/types/wb_iface.hpp>
+#include <boost/noncopyable.hpp>
namespace uhd { namespace usrp { namespace dboard { namespace twinrx {
class twinrx_ctrl : public boost::noncopyable {
public:
- typedef boost::shared_ptr<twinrx_ctrl> sptr;
+ typedef std::shared_ptr<twinrx_ctrl> sptr;
static sptr make(
dboard_iface::sptr db_iface,
twinrx_gpio::sptr gpio_iface,
- twinrx_cpld_regmap::sptr cpld_regmap);
+ twinrx_cpld_regmap::sptr cpld_regmap,
+ dboard_id_t rx_id
+ );
virtual ~twinrx_ctrl() {}
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ids.hpp b/host/lib/usrp/dboard/twinrx/twinrx_ids.hpp
new file mode 100644
index 000000000..599b1d456
--- /dev/null
+++ b/host/lib/usrp/dboard/twinrx/twinrx_ids.hpp
@@ -0,0 +1,16 @@
+
+#ifndef _TWINRX_IDS_HPP
+#define _TWINRX_IDS_HPP
+
+#include <uhd/usrp/dboard_id.hpp>
+
+using namespace uhd;
+using namespace usrp;
+
+namespace twinrx {
+ const dboard_id_t TWINRX_REV_A_ID(0x91);
+ const dboard_id_t TWINRX_REV_B_ID(0x93);
+ const dboard_id_t TWINRX_REV_C_ID(0x95);
+}
+
+#endif