diff options
Diffstat (limited to 'mpm/python')
| -rw-r--r-- | mpm/python/pyusrp_periphs.cpp | 2 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/__init__.py | 4 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/eiscat.py | 354 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py | 228 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/periph_manager/base.py | 2 | 
5 files changed, 583 insertions, 7 deletions
| diff --git a/mpm/python/pyusrp_periphs.cpp b/mpm/python/pyusrp_periphs.cpp index 5aac12088..c48860ebe 100644 --- a/mpm/python/pyusrp_periphs.cpp +++ b/mpm/python/pyusrp_periphs.cpp @@ -33,6 +33,7 @@  #include <mpm/ad937x/ad937x_ctrl.hpp>  #include <mpm/chips/lmk04828_spi_iface.hpp>  #include <mpm/dboards/magnesium_manager.hpp> +#include <mpm/dboards/eiscat_manager.hpp>  #include <boost/noncopyable.hpp>  namespace bp = boost::python; @@ -47,4 +48,5 @@ BOOST_PYTHON_MODULE(libpyusrp_periphs)      export_mykonos();      export_xbar();      export_magnesium(); +    export_eiscat();  } diff --git a/mpm/python/usrp_mpm/dboard_manager/__init__.py b/mpm/python/usrp_mpm/dboard_manager/__init__.py index e1ae5d3e0..93de24695 100644 --- a/mpm/python/usrp_mpm/dboard_manager/__init__.py +++ b/mpm/python/usrp_mpm/dboard_manager/__init__.py @@ -19,11 +19,11 @@ dboards module __init__.py  """  from .. import libpyusrp_periphs as lib  from .magnesium import Magnesium -from .eiscat import eiscat +from .eiscat import EISCAT  from .test import test  from .unknown import unknown  HW_PIDS = { -    eiscat.hw_pid: eiscat, +    EISCAT.hw_pid: EISCAT,      Magnesium.hw_pid: Magnesium,  } diff --git a/mpm/python/usrp_mpm/dboard_manager/eiscat.py b/mpm/python/usrp_mpm/dboard_manager/eiscat.py index 436307c1e..4534e124d 100644 --- a/mpm/python/usrp_mpm/dboard_manager/eiscat.py +++ b/mpm/python/usrp_mpm/dboard_manager/eiscat.py @@ -17,13 +17,359 @@  """  EISCAT rx board implementation module  """ + +import time +from six import iteritems +from ..mpmlog import get_logger +from ..uio import UIO  from . import lib  from .base import DboardManagerBase +from .lmk_eiscat import LMK04828EISCAT + +N_CHANS = 8 # Chans per dboard + +# Power enable pins +POWER_ENB = 0x200C # Address of the power enable register +PWR_CHAN_EN_2V5 = [ (1<<x) for x in xrange(8) ] +PWR2_5V_DC_CTRL_ENB = 1<<8 +PWR2_5V_DC_PWR_EN = 1<<9 +PWR2_5V_LNA_CTRL_EN = 1<<10 +PWR2_5V_LMK_SPI_EN = 1<<11 +PWR2_5V_ADC0_SPI_EN = 1<<12 +PWR2_5V_ADC1_SPI_EN = 1<<13 + +class ADS54J56(object): +    """ +    Controls for ADS54J56 ADC +    """ +    def __init__(self, regs, log): +        self.log = log +        self.regs = regs + +    def reset(self): +        """ +        Perform reset sequence +        """ +        self.log.trace("Resetting ADS54J56...") +        self.regs.poke8(0x000000, 0x81) # Analog reset +        self.regs.poke8(0x004004, 0x68) # Page = Main Digital +        self.regs.poke8(0x004003, 0x00) # Page = Main Digital +        self.regs.poke8(0x004002, 0x00) # Page = Main Digital +        self.regs.poke8(0x004001, 0x00) # Page = Main Digital +        self.regs.poke8(0x0060F7, 0x01) # Digital top reset +        self.regs.poke8(0x0070F7, 0x01) # Digital top reset +        self.regs.poke8(0x006000, 0x01) # Reset Digital (IL RESET) +        self.regs.poke8(0x007000, 0x01) # Reset Digital (IL RESET) +        self.regs.poke8(0x006000, 0x00) # Clear Reset +        self.regs.poke8(0x007000, 0x00) # Clear Reset +        self.regs.poke8(0x000011, 0x80) # Select Master page in Analog Bank +        self.regs.poke8(0x000053, 0x80) # Set clk divider to div-2 +        self.regs.poke8(0x000039, 0xC0) # ALWAYS WRITE 1 to this bit +        self.regs.poke8(0x000059, 0x20) # ALWAYS WRITE 1 to this bit + + +    # def setup(self): +# 11	80	0	0	Select Master page in Analog Bank +# 53	80	0	0	Set clk divider to div-2 +# 39	C0	0	0	ALWAYS WRITE 1 to this bit +# 59	20	0	0	ALWAYS WRITE 1 to this bit +# 4004	68	0	0	 +# 4003	0	0	0	 +# 6000	1	0	0	Reset interleaving engine for Ch A-B +# 6000	0	0	0	 +# 7000	1	0	0	Reset interleaving engine for Ch C-D +# 7000	0	0	0	 +# 4004	61	0	0	Select decimation filter page of JESD bank. +# 4003	41	0	0	 +# 6000	E4	0	0	DDC Mode 4 for A-B and E = CH A/B N value +# 7000	E4	0	0	DDC Mode 4 for A-B and E = CH A/B N value +# 6001	4	0	0	ALWAYS WRITE 1 to this bit +# 7001	4	0	0	ALWAYS WRITE 1 to this bit +# 6002	0E	0	0	Ch A/D N value +# 7002	0E	0	0	Ch A/D N value +# 4003	0	0	0	Select analog page in JESD Bank +# 4004	6A	0	0	 +# 6016	2	0	0	PLL mode 40x for A-B +# 7016	2	0	0	PLL mode 40x for C-D +# 4003	0	0	0	Select digital page in JESD Bank +# 4004	69	0	0	 +# 6000	40	0	0	Enable JESD Mode control for A-B +# 6001	2	0	0	Set JESD Mode to 40x for LMFS=2441 +# 7000	40	0	0	Enable JESD Mode control for C-D +# 7001	2	0	0	Set JESD Mode to 40x for LMFS=2441 +# 6000	80	0	0	Set CTRL K for A-B +# 6006	0F	0	0	Set K to 16 +# 7000	80	0	0	Set CTRL K for C-D +# 7006	0F	0	0	Set K to 16 +# 4005	1	0	0	Disable broadcast mode +# 7001	20	0	0	SyncbAB to issue a SYNC request for all 4 channels +# 4005	0	0	0	Enable broadcast mode + + +class MMCM(object): +    """ +    Controller for the MMCM inside the FPGA +    """ +    RADIO_CLK_CTRL = 0x2000 # Register address + +    RADIO_CLK1X_ENABLE = 1<<0 +    RADIO_CLK2X_ENABLE = 1<<1 +    RADIO_CLK3X_ENABLE = 1<<2 +    RADIO_CLK_MMCM_RESET = 1<<3 +    RADIO_CLK_VALID = 1<<4 + +    def __init__(self, regs, log): +        self.log = log +        self.regs = regs +        self.addr = self.RADIO_CLK_CTRL +        self.poke32 = lambda bits: self.regs.poke32(self.addr, bits) +        self.peek32 = lambda: self.regs.peek32(self.addr) + +    def reset(self): +        """ +        Uninitialize and reset the MMCM +        """ +        self.log.trace("Resetting MMCM, disabling all clocks...") +        self.poke32(self.RADIO_CLK_MMCM_RESET) + +    def enable(self): +        """ +        Unreset MMCM and poll lock indicators +        """ +        self.log.trace("Unresetting MMCM...") +        self.poke32(0x0000) # Take out of reset +        time.sleep(0.5) # Replace with poll and timeout TODO +        mmcm_locked = bool(self.peek32() & self.RADIO_CLK_VALID) +        if not mmcm_locked: +            self.log.error("MMCM not locked!") +            raise RuntimeError("MMCM not locked!") +        self.log.trace("Enabling output clocks on MMCM...") +        self.poke32( # Enable all the output clocks: +            self.RADIO_CLK1X_ENABLE | \ +            self.RADIO_CLK2X_ENABLE | \ +            self.RADIO_CLK3X_ENABLE +        ) +        return True + +class JesdCoreEiscat(object): +    CORE_ID_BASE = 0x4A455344 +    ADDR_BASE = 0x0000 +    ADDR_OFFSET = 0x1000 -class eiscat(DboardManagerBase): +# Check Core	100	4a455344	1	FFFFFFFF	check signature +	# 104		1		date should match the export number of the core. format: yymmddhh +					 +# GT PLL Power Control	C	FFFC0000	0	0	Power down unused CPLLs and QPLLs +					 +# GT PLL Lock Control	4	11111111	0	0	Reset CPLLs +	# 4	11111100	0	0	Unreset the ones we're using +	# wait 2 ms - alternatively, poll on the locked bit below				 +	# 10	10000	0	0	Clear all CPLL sticky bits +	# 4	22	1	FF	Read CPLL locked and no unlocked stickies. Error: GT PLL failed to lock. +					 +# GT RX Reset Routine	24	10	0	0	Place the RX MGTs in reset +	# 24	20	0	0	Unreset and Enable +	# 24	F0000	1	FFFF0000	Poll for reset complete for 20 ms. b1111 = number of GTs we use. Error: TX MGTs failed to reset +					 +# Init FPGA JESD204B Deframer (RX)	40	2	0	0	Force assertion of ADC SYNC +	# 50	0	0	0	Data = 0 = Scrambler enabled. Data = 1 = disabled. Must match ADC settings. Set to 1 for now +	# GT RX Reset Routine (full sequence)				 +	# 50	0	0	0	Stop forcing assertion of ADC SYNC +					 +# Check Deframer Status (RX)	40	3000001C	1	FFFFFFFF	 + +    def __init__(self, regs, slot, core_idx, log): +        self.log = log +        self.regs = regs +        self.slot = slot +        assert core_idx in (0, 1) +        self.core_idx = core_idx +        self.base_addr = self.ADDR_BASE + self.ADDR_OFFSET * self.core_idx +        self.log.trace("Slot: {} JESD Core {}: Base address {}".format( +            self.slot, self.core_idx, self.base_addr +        )) +        self.peek32 = lambda addr: self.regs.peek32(self.base_addr + addr) +        self.poke32 = lambda addr, data: self.regs.poke32(self.base_addr + addr, data) +        if not self.check_core_id(): +            raise RuntimeError("Could not identify JESD core!") + +    def check_core_id(self): +        """ +        Verify that the JESD core ID is correct. +        """ +        expected_id = self.CORE_ID_BASE + self.core_idx +        core_id = self.peek32(0x100) +        self.log.trace("Reading JESD core ID: {:x}".format(core_id)) +        if core_id != expected_id: +            self.log.error( +                "Cannot identify JESD core! Read ID: {:x} Expected: {:x}".format( +                    core_id, expected_id +                ) +            ) +            return False +        date_info = core_id = self.peek32(0x104) +        self.log.trace("Reading JESD date info: {:x}".format(date_info)) +        return True + +    def init(self): +        """ +        Run initialization sequence on JESD core. +        """ +        self._gt_pll_power_control() +        self._gt_rx_reset(True) +        if not self._gt_pll_lock_control(): +            raise RuntimeError("JESD CORE {} PLLs not locked!".format(self.core_idx)) + +    def _gt_pll_power_control(self): +        """ +        Power down unused CPLLs and QPLLs +        """ +        self.poke32(0x00C, 0xFFFC0000) + +    def _gt_rx_reset(self, reset_only=True): +        """ +        RX Reset +        """ +        self.poke32(0x024, 0x10) # Place the RX MGTs in reset +        if not reset_only: +            time.sleep(.001) # Probably not necessary +            self.poke32(0x024, 0x20) # Unreset and Enable +            time.sleep(0.1) # TODO replace with poll and timeout 20 ms +            lock_status = self.peek32(0x024) & 0xFFFF0000 +            if lock_status != 0xF0000: +                self.log.error( +                    "Error: TX MGTs failed to reset! Status: 0x{:x}".format(lock_status) +                ) + +    def _gt_pll_lock_control(self): +        """ +        Make sure PLLs are locked +        """ +        self.poke32(0x004, 0x11111111) # Reset CPLLs +        self.poke32(0x004, 0x11111100) # Unreset the ones we're using +        time.sleep(0.002) # TODO replace with poll and timeout +        self.poke32(0x010, 0x10000) # Clear all CPLL sticky bits +        lock_status = self.peek32(0x004) & 0xFF +        lock_good = bool(lock_status == 0x22) +        if not lock_good: +            self.log.error("GT PLL failed to lock! Status: 0x{:x}".format(lock_status)) +        return lock_good + +    def _gt_polarity_control(self): +        """ +        foo +        """ +        reg_val = { +            'A': {0: 0x00, 1: 0x11}, +            'B': {0: 0x01, 1: 0x01}, +        } +        self.log.trace( +            "JESD Core: Slot {}, ADC {}: Setting polarity control to 0b{:2b}".format( +                self.slot, self.core_idx, reg_val +            )) +        self.poke32(0x80, reg_val) + +class EISCAT(DboardManagerBase): +    """ +    EISCAT Daughterboard +    """      hw_pid = 3      special_eeprom_addrs = {"special0": "something"} +    spi_chipselect = { +        "lmk": 0, +        "adc0": 1, +        "adc1": 2 +    } + +    def __init__(self, spi_devices, *args, **kwargs): +        super(EISCAT, self).__init__(*args, **kwargs) +        self.log = get_logger("EISCAT") +        self.log.trace("Initializing EISCAT daughterbaord") +        if len(spi_devices) < len(self.spi_chipselect): +            self.log.error("Expected {0} spi devices, found {1} spi devices".format( +                len(self.spi_chipselect), len(spi_devices), +            )) +            raise RuntimeError("Not enough SPI devices found.") +        self._spi_nodes = {} +        for k, v in iteritems(self.spi_chipselect): +            self._spi_nodes[k] = spi_devices[v] +        self.log.debug("spidev device node map: {}".format(self._spi_nodes)) + +    def init_device(self): +        """ +        Execute necessary actions to bring up the daughterboard +        """ +        self.log.debug("Loading C++ drivers...") +        self._device = lib.eiscat.eiscat_manager( +            self._spi_nodes['lmk'], +            self._spi_nodes['adc0'], +            self._spi_nodes['adc1'], +            # self._spi_nodes['phasedac'], +        ) +        self.lmk_regs = self._device.get_clock_ctrl() +        self.adc0_regs = self._device.get_adc0_ctrl() +        self.adc1_regs = self._device.get_adc1_ctrl() +        self.spi_lock = self._device.get_spi_lock() +        self.log.debug("Loaded C++ drivers.") +        self.log.debug("Getting uio...") +        self.radio_regs = UIO(label="jesd204b-regs", read_only=False) +        jesd_id = self.radio_regs.peek32(0x100) +        self.log.trace("Reading JESD core ID: {:x}".format(jesd_id)) +        if jesd_id != 0x4A455344: +            self.log.error("Cannot identify JESD core 0! Read ID: {:x}".format(jesd_id)) +        jesd_id = self.radio_regs.peek32(0x1100) +        self.log.trace("Reading JESD core ID: {:x}".format(jesd_id)) +        if jesd_id != 0x4A455345: +            self.log.error("Cannot identify JESD core 1! Read ID: {:x}".format(jesd_id)) +            self.log.error("Cannot identify JESD core! Read ID: {:x}".format(jesd_id)) +        jesd_id = self.radio_regs.peek32(0x1100) +        self.log.trace("Reading JESD core ID: {:x}".format(jesd_id)) +        if jesd_id != 0x4A455345: +            self.log.error("Cannot identify JESD core! Read ID: {:x}".format(jesd_id)) +        self.log.info("Radio-register UIO object successfully generated!") +        self.mmcm = MMCM(self.radio_regs, self.log) +        self.init_power(self.radio_regs) +        self.mmcm.reset() +        self.lmk = LMK04828EISCAT(self.lmk_regs, self.spi_lock, "A") # Initializes LMK +        if not self.mmcm.enable(): +            self.log.error("Could not re-enable MMCM!") +            raise RuntimeError("Could not re-enable MMCM!") +        self.log.info("MMCM enabled!") +        self.adc0 = ADS54J56(self.adc0_regs, self.log) +        self.adc1 = ADS54J56(self.adc1_regs, self.log) +        self.adc0.reset() +        self.adc1.reset() +        self.log.info("ADCs resetted!") +# Send SYSREF		Pulse the "f2SendSysRefToAdcA" and "f2SendSysRefToAdcB" signals in the JESD nelist for 1 FpgaClk2x cycle. This will eventually be a timed command from the Radio. +# Initialize ADC -A		These are all SPI transactions. +# Initialize ADC -B		See the "ADC Setup" tab. +# Init FPGA JESD204B Deframer (RX) -A		See function call in "JESD204b FPGA Core Setup" tab. +# Init FPGA JESD204B Deframer (RX) -B		Same as -A. +# Send SYSREF		Same as the first Send SYSREF call. +# Check Deframer Status (RX) -A		See function call in "JESD204b FPGA Core Setup" tab. +# Check Deframer Status (RX) -B		Same as -A. + +    def init_power(self, regs): +        """ +        Turn on power to the dboard. + +        After this function, we should never touch this register again. +        """ +        reg_val = PWR2_5V_DC_CTRL_ENB +        self.log.trace("Asserting power ctrl enable ({})...".format(bin(reg_val))) +        regs.poke32(POWER_ENB, reg_val) +        time.sleep(0.001) +        reg_val = reg_val \ +                | PWR2_5V_DC_CTRL_ENB \ +                | PWR2_5V_DC_PWR_EN \ +                | PWR2_5V_LNA_CTRL_EN \ +                | PWR2_5V_LMK_SPI_EN | PWR2_5V_ADC0_SPI_EN #| PWR2_5V_ADC1_SPI_EN +        regs.poke32(POWER_ENB, reg_val) +        self.log.trace("Asserting power enable for all the chips ({})...".format(bin(reg_val))) +        time.sleep(0.1) +        for chan in xrange(8): +            reg_val = reg_val | PWR_CHAN_EN_2V5[chan] +        self.log.trace("Asserting power enable for all the channels ({})...".format(bin(reg_val))) +        regs.poke32(POWER_ENB, reg_val) + -    def __init__(self, spidevs=[], *args, **kwargs): -        # Do own init -        super(eiscat, self).__init__(*args, **kwargs) diff --git a/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py b/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py new file mode 100644 index 000000000..733f8fbef --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py @@ -0,0 +1,228 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# 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/>. +# +""" +LMK04828 driver for use with Magnesium +""" + +import time +from ..mpmlog import get_logger + +LMK_CHIP_ID = 6 + +class LMK04828EISCAT(object): +    """ +    LMK04828 controls for EISCAT daughterboard +    """ +    def __init__(self, regs_iface, spi_lock, slot=None): +        slot = slot or "-A" +        self.log = get_logger("LMK04828"+slot) +        self.regs_iface = regs_iface +        self.spi_lock = spi_lock +        self.init() +        self.config() + +    def pokes8(self, addr_vals): +        """ +        Apply a series of pokes +        """ +        for addr, val in addr_vals: +            self.regs_iface.poke8(addr, val) + +    def init(self): +        """ +        Basic init. Turns it on. Let's us read SPI. +        """ +        self.log.info("Init LMK") +        self.pokes8(( +            (0x000, 0x90), # Assert reset +            (0x000, 0x10), # De-assert reset +            (0x002, 0x00), # De-assert power down +            (0x16E, 0x3B), # PLL2 Lock Detect Config as SDO +        )) +        if not self.verify_chip_id(): +            raise Exception("Unable to locate LMK04828") + + +    def config(self): +        """ +        Write lots of config foo. +        """ +        self.log.trace("Setting clkout config...") +        self.pokes8(( +            (0x100, 0x6C), # CLKout Config +            (0x101, 0x55), # CLKout Config +            (0x103, 0x00), # CLKout Config +            (0x104, 0x20), # CLKout Config +            (0x105, 0x00), # CLKout Config +            (0x106, 0xF3), # CLKout Config +            (0x107, 0x05), # CLKout Config +            (0x108, 0x6C), # CLKout Config +            (0x109, 0x55), # CLKout Config +            (0x10B, 0x00), # CLKout Config +            (0x10C, 0x20), # CLKout Config +            (0x10D, 0x00), # CLKout Config +            (0x10E, 0xF1), # CLKout Config +            (0x10F, 0x05), # CLKout Config +            (0x110, 0x6C), # CLKout Config +            (0x111, 0x55), # CLKout Config +            (0x113, 0x00), # CLKout Config +            (0x114, 0x20), # CLKout Config +            (0x115, 0x00), # CLKout Config +            (0x116, 0xF1), # CLKout Config +            (0x117, 0x05), # CLKout Config +            (0x118, 0x6C), # CLKout Config +            (0x119, 0x55), # CLKout Config +            (0x11B, 0x00), # CLKout Config +            (0x11C, 0x20), # CLKout Config +            (0x11D, 0x00), # CLKout Config +            (0x11E, 0xF1), # CLKout Config +            (0x11F, 0x05), # CLKout Config +            (0x120, 0x78), # CLKout Config +            (0x121, 0x55), # CLKout Config +            (0x123, 0x00), # CLKout Config +            (0x124, 0x20), # CLKout Config +            (0x125, 0x00), # CLKout Config +            (0x126, 0xF3), # CLKout Config +            (0x127, 0x00), # CLKout Config +            (0x128, 0x6C), # CLKout Config +            (0x129, 0x55), # CLKout Config +            (0x12B, 0x00), # CLKout Config +            (0x12C, 0x20), # CLKout Config +            (0x12D, 0x00), # CLKout Config +            (0x12E, 0xF9), # CLKout Config +            (0x12F, 0x00), # CLKout Config +            (0x130, 0x6C), # CLKout Config +            (0x131, 0x55), # CLKout Config +            (0x133, 0x00), # CLKout Config +            (0x134, 0x20), # CLKout Config +            (0x135, 0x00), # CLKout Config +            (0x136, 0xF9), # CLKout Config +            (0x137, 0x00), # CLKout Config +            (0x138, 0x10), # VCO_MUX to VCO 1; OSCout off +            (0x139, 0x00), # SYSREF Source = MUX; SYSREF MUX = Normal SYNC +            (0x13A, 0x01), # SYSREF Divide [12:8] +            (0x13B, 0xE0), # SYSREF Divide [7:0] +            (0x13C, 0x00), # SYSREF DDLY [12:8] +            (0x13D, 0x08), # SYSREF DDLY [7:0] ... 8 is default, <8 is reserved +            (0x13E, 0x00), # SYSREF Pulse Count = 1 pulse/request +            (0x13F, 0x0B), # Feedback Mux: Enabled, DCLKout6, drives PLL1N divider +            (0x140, 0x00), # POWERDOWN options +            (0x141, 0x00), # Dynamic digital delay enable +            (0x142, 0x00), # Dynamic digital delay step +            (0x143, 0xD1), # SYNC edge sensitive; SYSREF_CLR; SYNC Enabled; SYNC fro +            (0x144, 0x00), # Enable SYNC on all outputs including sysref +            (0x145, 0x7F), # Always program to d127 +            (0x146, 0x08), # CLKin Type & En +            (0x147, 0x0E), # CLKin_SEL = CLKin1 manual; CLKin1 to PLL1 +            (0x148, 0x01), # CLKin_SEL0 = input with pullup +            (0x149, 0x01), # CLKin_SEL1 = input with pulldown +            (0x14A, 0x02), # RESET type as input w/pulldown +            (0x14B, 0x01), # Holdover & DAC Manual Mode +            (0x14C, 0xF6), # DAC Manual Mode +            (0x14D, 0x00), # DAC Settings (defaults) +            (0x14E, 0x00), # DAC Settings (defaults) +            (0x14F, 0x7F), # DAC Settings (defaults) +            (0x150, 0x03), # Holdover Settings (defaults) +            (0x151, 0x02), # Holdover Settings (defaults) +            (0x152, 0x00), # Holdover Settings (defaults) +            (0x153, 0x00), # CLKin0_R divider [13:8], default = 0 +            (0x154, 0x0A), # CLKin0_R divider [7:0], default = d120 +            (0x155, 0x00), # CLKin1_R divider [13:8], default = 0 +            (0x156, 0x01), # CLKin1_R divider [7:0], default = d120 +            (0x157, 0x00), # CLKin2_R divider [13:8], default = 0 +            (0x158, 0x01), # CLKin2_R divider [7:0], default = d120 +            (0x159, 0x00), # PLL1 N divider [13:8], default = 0 +            (0x15A, 0x68), # PLL1 N divider [7:0], default = d120 +            (0x15B, 0xCF), # PLL1 PFD +            (0x15C, 0x27), # PLL1 DLD Count [13:8] +            (0x15D, 0x10), # PLL1 DLD Count [7:0] +            (0x15E, 0x00), # PLL1 R/N delay, defaults = 0 +            (0x15F, 0x13), # Status LD1 pin = PLL2 LD, push-pull output +            (0x160, 0x00), # PLL2 R divider [11:8]; +            (0x161, 0x01), # PLL2 R divider [7:0] +            (0x162, 0x24), # PLL2 prescaler; OSCin freq +            (0x163, 0x00), # PLL2 Cal = PLL2 normal val +            (0x164, 0x00), # PLL2 Cal = PLL2 normal val +            (0x165, 0x0C), # PLL2 Cal = PLL2 normal val +            (0x171, 0xAA), # Write this val after x165 +            (0x172, 0x02), # Write this val after x165 +            (0x17C, 0x15), # VCo1 Cal; write before x168 +            (0x17D, 0x33), # VCo1 Cal; write before x168 +            (0x166, 0x00), # PLL2 N[17:16] +            (0x167, 0x00), # PLL2 N[15:8] +            (0x168, 0x0C), # PLL2 N[7:0] +            (0x169, 0x51), # PLL2 PFD +            (0x16A, 0x00), # PLL2 DLD Count [13:8] = default d32 +            (0x16B, 0x10), # PLL2 DLD Count [7:0] = default d0 +            (0x16C, 0x00), # PLL2 Loop filter r = 200 ohm +            (0x16D, 0x00), # PLL2 loop filter c = 10 pF +            (0x173, 0x00), # Do not power down PLL2 or prescaler +        )) +        time.sleep(0.1) +        self.pokes8(( +            (0x182, 0x1), # Clear Lock Detect Sticky +            (0x182, 0x0), # Clear Lock Detect Sticky +            (0x183, 0x1), # Clear Lock Detect Sticky +            (0x183, 0x0), # Clear Lock Detect Sticky +        )) +        time.sleep(0.1) +        self.log.trace("Checking PLL lock bits...") +        def check_pll_lock(pll_id, addr): +            pll_lock_status = self.regs_iface.peek8(addr) +            if (pll_lock_status & 0x7) != 0x02: +                self.log.error("LMK {} did not lock. Status: {:x}".format(pll_id, pll_lock_status)) +                raise RuntimeError("LMK {} did not lock.".format(pll_id)) +        check_pll_lock("PLL1", 0x182) +        check_pll_lock("PLL2", 0x183) +        self.log.trace("Setting SYNC and SYSREF config...") +        self.pokes8(( +            (0x143, 0xF1), # toggle SYNC polarity to trigger SYNC event +            (0x143, 0xD1), # toggle SYNC polarity to trigger SYNC event +            (0x139, 0x02), # SYSREF Source = MUX; SYSREF MUX = pulser +            (0x144, 0xFF), # Disable SYNC on all outputs including sysref +            (0x143, 0x52), # Pulser selected; SYNC enabled; 1 shot enabled +        )) +        self.log.info("LMK init'd and locked!") + +    def get_chip_id(self): +        """ +        Read back the chip ID +        """ +        chip_id = self.regs_iface.peek8(0x03) +        self.log.trace("Read chip ID: {}".format(chip_id)) +        return chip_id + +    def verify_chip_id(self): +        """ +        Returns True if the chip ID matches what we expect, False otherwise. +        """ +        chip_id = self.get_chip_id() +        if chip_id != LMK_CHIP_ID: +            self.log.error("wrong chip id {0}".format(chip_id)) +            return False +        return True + +    # TODO delete this +    # def enable_sysref_pulse(self): +        # """ +        # Enable SYSREF pulses +        # """ +        # self.spi_lock.lock() +        # self.poke8(0x139, 0x2) +        # self.poke8(0x144, 0xFF) +        # self.poke8(0x143, 0x52) +        # self.spi_lock.unlock() diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py index 4815bd88d..25f635c68 100644 --- a/mpm/python/usrp_mpm/periph_manager/base.py +++ b/mpm/python/usrp_mpm/periph_manager/base.py @@ -75,7 +75,7 @@ class PeriphManagerBase(object):          # eeprom_data = EEPROM().read_eeprom(get_eeprom_path(eeprom_addr))          eeprom_data = None          # I know spidev masters on the dboard slots -        hw_pid = 2 +        hw_pid = 3          if hw_pid in dboard_manager.HW_PIDS:              spi_devices = sorted(get_spidev_nodes("e0006000.spi"))              self.log.debug("Found spidev nodes: {0}".format(spi_devices)) | 
