diff options
| author | Brent Stapleton <brent.stapleton@ettus.com> | 2018-04-17 13:15:38 -0700 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2018-06-27 10:22:25 -0700 | 
| commit | 4f49b4a937c0f6724828fa6ecfa9b14cc0f23f34 (patch) | |
| tree | 8d2db9a33b03123669f9b9446cf9547cf4da2baa /mpm/python/usrp_mpm/chips | |
| parent | 74c41781390ba0352431167d97ecec22c88e2336 (diff) | |
| download | uhd-4f49b4a937c0f6724828fa6ecfa9b14cc0f23f34.tar.gz uhd-4f49b4a937c0f6724828fa6ecfa9b14cc0f23f34.tar.bz2 uhd-4f49b4a937c0f6724828fa6ecfa9b14cc0f23f34.zip  | |
mpm: adding adf400x support to chips
Adding ADF400X driver to MPM. This uses the Boost.Python bound spidev,
and is largely a translation from the C++ driver in UHD.
Diffstat (limited to 'mpm/python/usrp_mpm/chips')
| -rw-r--r-- | mpm/python/usrp_mpm/chips/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/chips/__init__.py | 1 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/chips/adf400x.py | 213 | 
3 files changed, 216 insertions, 2 deletions
diff --git a/mpm/python/usrp_mpm/chips/CMakeLists.txt b/mpm/python/usrp_mpm/chips/CMakeLists.txt index ffe1b2419..94b62f33d 100644 --- a/mpm/python/usrp_mpm/chips/CMakeLists.txt +++ b/mpm/python/usrp_mpm/chips/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2017 Ettus Research, National Instruments Company +# Copyright 2017-2018 Ettus Research, a National Instruments Company  #  # SPDX-License-Identifier: GPL-3.0  # @@ -8,7 +8,7 @@ SET(USRP_MPM_FILES ${USRP_MPM_FILES})  SET(USRP_MPM_CHIP_FILES      ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py      ${CMAKE_CURRENT_SOURCE_DIR}/lmk04828.py +    ${CMAKE_CURRENT_SOURCE_DIR}/adf400x.py  )  LIST(APPEND USRP_MPM_FILES ${USRP_MPM_CHIP_FILES})  SET(USRP_MPM_FILES ${USRP_MPM_FILES} PARENT_SCOPE) - diff --git a/mpm/python/usrp_mpm/chips/__init__.py b/mpm/python/usrp_mpm/chips/__init__.py index c8d50d714..15b4a3704 100644 --- a/mpm/python/usrp_mpm/chips/__init__.py +++ b/mpm/python/usrp_mpm/chips/__init__.py @@ -7,4 +7,5 @@  Chips submodule  """ +from .adf400x import ADF400x  from .lmk04828 import LMK04828 diff --git a/mpm/python/usrp_mpm/chips/adf400x.py b/mpm/python/usrp_mpm/chips/adf400x.py new file mode 100644 index 000000000..4a33a33a9 --- /dev/null +++ b/mpm/python/usrp_mpm/chips/adf400x.py @@ -0,0 +1,213 @@ +# +# 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() +        self.adf400x_regs.ref_counter = 1 +        self.adf400x_regs.charge_pump_current_1 = 7 +        self.adf400x_regs.charge_pump_current_2 = 7 +        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 +        self.adf400x_regs.charge_pump_mode = ADF400xRegs.CHARGE_PUMP_TRISTATE +        # Set the N counter +        if freq is None: +            freq = DEFAULT_REF_CLOCK_FREQ +        self._set_n_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_n_counter(self, freq): +        n_counter = int(BASE_REF_CLOCK_FREQ / freq) +        if self.adf400x_regs.n_counter == n_counter: +            self.log.trace("No change to N counter value ({}); returning early".format(n_counter)) +            return +        self.log.trace("Setting N counter to {}".format(n_counter)) +        # Limits from the datasheet +        assert 1 <= n_counter <= 8191 +        self.adf400x_regs.n_counter = n_counter + +    def set_ref_freq(self, freq): +        """Set the input reference frequency""" +        self._set_n_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  | 
