uhd/mpm/python/usrp_mpm/chips/adf400x.py
Sugandha Gupta 61ac937294 adf400x: Fix adf400x driver for ref counter and charge pump mode
- For different ref clock frequencies, the ref_counter should change
and not the n_counter.
- The charge pump should be set to normal mode and tristate as that
would prevent the PLL to lock.
2018-08-02 16:07:15 -07:00

221 lines
7.4 KiB
Python

#
# Copyright 2018 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
ADF400x driver class
Compatible with ADF4001 and ADF4002.
"""
from builtins import object
from usrp_mpm.mpmlog import get_logger
BASE_REF_CLOCK_FREQ = 40e6
DEFAULT_REF_CLOCK_FREQ = 20e6
class ADF400x(object):
"""
Generic driver class for ADF4002 access.
Inputs:
freq : frequency of reference input
parent_log : logger of parent
"""
def __init__(self, regs_iface, freq=None, parent_log=None):
self.log = \
parent_log.getChild("ADF400x") if parent_log is not None \
else get_logger("ADF400x")
self.regs_iface = regs_iface
assert hasattr(self.regs_iface, 'transfer24_8')
self.transfer24_8 = regs_iface.transfer24_8
# Instantiate our own copy of the register mapping and update some values
self.adf400x_regs = ADF400xRegs()
# N counter = fVCO/PFD
self.adf400x_regs.n_counter = 4
self.adf400x_regs.charge_pump_current_1 = 7
self.adf400x_regs.charge_pump_current_2 = 7
# Set MUXOUT to Digital Lock Detect
self.adf400x_regs.muxout = ADF400xRegs.MUXOUT_DLD
self.adf400x_regs.counter_reset = ADF400xRegs.COUNTER_RESET_NORMAL
self.adf400x_regs.phase_detector_polarity = ADF400xRegs.PHASE_DETECTOR_POLARITY_POS
# Turn on Charge Pump
self.adf400x_regs.charge_pump_mode = ADF400xRegs.CHARGE_PUMP_NORMAL
# Set the N counter
if freq is None:
freq = DEFAULT_REF_CLOCK_FREQ
self._set_ref_counter(freq)
# Now initialize the ADF400x
self.program_regs()
def program_regs(self):
"""
Run through the programming sequence
"""
# No control over CE, only LE, therefore we use the initialization latch method
self._write_reg(3)
# Conduct a function latch (2)
self._write_reg(2)
# Write R counter latch (0)
self._write_reg(0)
# Write N counter latch (1)
self._write_reg(1)
def _write_reg(self, addr):
"""Write the expected value to the given addr"""
reg_val = self.adf400x_regs.get_reg(addr)
self.log.trace("Writing {:06x} to spidev".format(reg_val))
self.transfer24_8(reg_val)
def set_lock_to_ext_ref(self, external):
"""Set the clock source to external"""
if bool(external):
self.adf400x_regs.charge_pump_mode = ADF400xRegs.CHARGE_PUMP_NORMAL
else:
self.adf400x_regs.charge_pump_mode = ADF400xRegs.CHARGE_PUMP_TRISTATE
self.program_regs()
def _set_ref_counter(self, freq):
"""
Set R Counter based on reference frequency
"""
# Calculate R counter fVCO = N * (fREF/R)
ref_counter = int(self.adf400x_regs.n_counter * freq / BASE_REF_CLOCK_FREQ)
if self.adf400x_regs.ref_counter == ref_counter:
self.log.trace("No change to ref counter value ({}); \
returning early".format(ref_counter))
return
self.log.trace("Setting ref counter to {}".format(ref_counter))
# Limits from the datasheet
assert 1 <= ref_counter <= 16383
self.adf400x_regs.ref_counter = ref_counter
def set_ref_freq(self, freq):
"""Set the input reference frequency"""
self._set_ref_counter(freq)
self.program_regs()
class ADF400xRegs(object):
"""Register map for ADF400x"""
# TODO: Move each field into an Enum or something
# TODO: Add setters/getters for each field
# anti backlash widths
ANTI_BACKLASH_WIDTH_2_9NS = 0
ANTI_BACKLASH_WIDTH_1_3NS = 1
ANTI_BACKLASH_WIDTH_6_0NS = 2
ANTI_BACKLASH_WIDTH_2_9NS_WAT = 3
# lock detect precision
LOCK_DETECT_PRECISION_3CYC = 0
LOCK_DETECT_PRECISION_5CYC = 1
# charge pump gain
CHARGE_PUMP_GAIN_1 = 0
CHARGE_PUMP_GAIN_2 = 1
# counter reset
COUNTER_RESET_NORMAL = 0
COUNTER_RESET_RESET = 1
# power down
POWER_DOWN_NORMAL = 0
POWER_DOWN_ASYNC = 1
POWER_DOWN_SYNC = 3
# muxout
MUXOUT_TRISTATE_OUT = 0
MUXOUT_DLD = 1
MUXOUT_NDIV = 2
MUXOUT_AVDD = 3
MUXOUT_RDIV = 4
MUXOUT_NCH_OD_ALD = 5
MUXOUT_SDO = 6
MUXOUT_GND = 7
# phase detector polarity
PHASE_DETECTOR_POLARITY_NEG = 0
PHASE_DETECTOR_POLARITY_POS = 1
# charge pump mode
CHARGE_PUMP_NORMAL = 0
CHARGE_PUMP_TRISTATE = 1
# fastlock mode
FASTLOCK_MODE_DISABLED = 0
FASTLOCK_MODE_1 = 1
FASTLOCK_MODE_2 = 2
# timer counter control
TIMEOUT_3CYC = 0
TIMEOUT_7CYC = 1
TIMEOUT_11CYC = 2
TIMEOUT_15CYC = 3
TIMEOUT_19CYC = 4
TIMEOUT_23CYC = 5
TIMEOUT_27CYC = 6
TIMEOUT_31CYC = 7
TIMEOUT_35CYC = 8
TIMEOUT_39CYC = 9
TIMEOUT_43CYC = 10
TIMEOUT_47CYC = 11
TIMEOUT_51CYC = 12
TIMEOUT_55CYC = 13
TIMEOUT_59CYC = 14
TIMEOUT_63CYC = 15
def __init__(self):
"""Set the default configuration"""
self.ref_counter = 0
self.n_counter = 0
self.charge_pump_current_1 = 0
self.charge_pump_current_2 = 0
self.anti_backlash_width = ADF400xRegs.ANTI_BACKLASH_WIDTH_2_9NS
self.lock_detect_precision = ADF400xRegs.LOCK_DETECT_PRECISION_3CYC
self.charge_pump_gain = ADF400xRegs.CHARGE_PUMP_GAIN_1
self.counter_reset = ADF400xRegs.COUNTER_RESET_NORMAL
self.power_down = ADF400xRegs.POWER_DOWN_NORMAL
self.muxout = ADF400xRegs.MUXOUT_TRISTATE_OUT
self.phase_detector_polarity = ADF400xRegs.PHASE_DETECTOR_POLARITY_NEG
self.charge_pump_mode = ADF400xRegs.CHARGE_PUMP_TRISTATE
self.fastlock_mode = ADF400xRegs.FASTLOCK_MODE_DISABLED
self.timer_counter_control = ADF400xRegs.TIMEOUT_3CYC
def get_reg(self, addr):
"""Get the register value to write to the given addr"""
reg = 0
if addr == 0:
reg |= (self.ref_counter & 0x003FFF) << 2
reg |= (self.anti_backlash_width & 0x000003) << 16
reg |= (self.lock_detect_precision & 0x000001) << 20
elif addr == 1:
reg |= (self.n_counter & 0x001FFF) << 8
reg |= (self.charge_pump_gain & 0x000001) << 21
elif addr == 2:
reg |= (self.counter_reset & 0x000001) << 2
reg |= (self.power_down & 0x000001) << 3
reg |= (self.muxout & 0x000007) << 4
reg |= (self.phase_detector_polarity & 0x000001) << 7
reg |= (self.charge_pump_mode & 0x000001) << 8
reg |= (self.fastlock_mode & 0x000003) << 9
reg |= (self.timer_counter_control & 0x00000F) << 11
reg |= (self.charge_pump_current_1 & 0x000007) << 15
reg |= (self.charge_pump_current_2 & 0x000007) << 18
reg |= (self.power_down & 0x000002) << 20
elif addr == 3:
reg |= (self.counter_reset & 0x000001) << 2
reg |= (self.power_down & 0x000001) << 3
reg |= (self.muxout & 0x000007) << 4
reg |= (self.phase_detector_polarity & 0x000001) << 7
reg |= (self.charge_pump_mode & 0x000001) << 8
reg |= (self.fastlock_mode & 0x000003) << 9
reg |= (self.timer_counter_control & 0x00000F) << 11
reg |= (self.charge_pump_current_1 & 0x000007) << 15
reg |= (self.charge_pump_current_2 & 0x000007) << 18
reg |= (self.power_down & 0x000002) << 20
reg |= addr
return reg