diff options
Diffstat (limited to 'mpm/python/usrp_mpm')
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/__init__.py | 1 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/eiscat.py | 731 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py | 220 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/unknown.py | 2 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n3xx.py | 3 | 
6 files changed, 1 insertions, 958 deletions
diff --git a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt index b01d220df..49ab58b40 100644 --- a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt +++ b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt @@ -17,12 +17,10 @@ set(USRP_MPM_DBMGR_FILES      ${CMAKE_CURRENT_SOURCE_DIR}/lmk_rh.py      ${CMAKE_CURRENT_SOURCE_DIR}/adc_rh.py      ${CMAKE_CURRENT_SOURCE_DIR}/dac_rh.py -    ${CMAKE_CURRENT_SOURCE_DIR}/eiscat.py      ${CMAKE_CURRENT_SOURCE_DIR}/neon.py      ${CMAKE_CURRENT_SOURCE_DIR}/e31x_db.py      ${CMAKE_CURRENT_SOURCE_DIR}/gain_rh.py      ${CMAKE_CURRENT_SOURCE_DIR}/gaintables_rh.py -    ${CMAKE_CURRENT_SOURCE_DIR}/lmk_eiscat.py      ${CMAKE_CURRENT_SOURCE_DIR}/lmk_mg.py      ${CMAKE_CURRENT_SOURCE_DIR}/magnesium.py      ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_update_cpld.py diff --git a/mpm/python/usrp_mpm/dboard_manager/__init__.py b/mpm/python/usrp_mpm/dboard_manager/__init__.py index 28a8ef80b..761ab57db 100644 --- a/mpm/python/usrp_mpm/dboard_manager/__init__.py +++ b/mpm/python/usrp_mpm/dboard_manager/__init__.py @@ -13,7 +13,6 @@ if not __simulated__:      from .rhodium import Rhodium      from .neon import Neon      from .e31x_db import E31x_db -    from .eiscat import EISCAT      from .empty_slot import EmptySlot      from .zbx import ZBX      from .test import test diff --git a/mpm/python/usrp_mpm/dboard_manager/eiscat.py b/mpm/python/usrp_mpm/dboard_manager/eiscat.py deleted file mode 100644 index ee304d77d..000000000 --- a/mpm/python/usrp_mpm/dboard_manager/eiscat.py +++ /dev/null @@ -1,731 +0,0 @@ -# -# Copyright 2017 Ettus Research, a National Instruments Company -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -""" -EISCAT rx board implementation module -""" - -import time -from builtins import range -from builtins import object -from usrp_mpm.mpmlog import get_logger -from usrp_mpm.sys_utils.uio import UIO -from usrp_mpm import lib -from usrp_mpm.dboard_manager import DboardManagerBase -from usrp_mpm.dboard_manager.lmk_eiscat import LMK04828EISCAT -from usrp_mpm.cores import ClockSynchronizer - -def create_spidev_iface_sane(dev_node): -    """ -    Create a regs iface from a spidev node (sane values) -    """ -    return lib.spi.make_spidev_regs_iface( -        str(dev_node), -        1000000, # Speed (Hz) -        3, # SPI mode -        8, # Addr shift -        0, # Data shift -        1<<23, # Read flag -        0, # Write flag -    ) - -def create_spidev_iface_phasedac(dev_node): -    """ -    Create a regs iface from a spidev node (ADS5681) -    """ -    return lib.spi.make_spidev_regs_iface( -        str(dev_node), -        1000000, # Speed (Hz) -        1, # SPI mode -        20, # Addr shift -        8, # Data shift -        0, # Read flag -        0, # Write flag -    ) - - -class ADS54J56(object): -    """ -    Controls for ADS54J56 ADC - -    These commands are very specific to the EISCAT daughterboard, so they stay -    here. -    """ -    def __init__(self, regs, log): -        self.log = log -        self.regs = regs -        self.sync_line = "AB" - -    def swap_sync_line(self, new_value=None): -        """ -        Select sync pin value, or switch sync pin over. If new_value is given, -        use that (it has to be either AB or CD), otherwise, pick whatever is -        currently not selected. -        """ -        if new_value is not None: -            self.sync_line = new_value -        elif self.sync_line == "AB": -            self.sync_line = "CD" -        else: -            self.sync_line = "AB" -        assert self.sync_line in ('AB', 'CD') -        self.log.debug( -            "The next setup() sequence will use sync pin: {}".format( -                self.sync_line -            ) -        ) - -    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 -        readback_test_addr = 0x11 -        readback_test_val = self.regs.peek8(readback_test_addr) -        self.log.trace("ADC readback reg 0x{:x} post-reset: 0x{:x}".format( -            readback_test_addr, -            readback_test_val, -        )) - - -    def setup(self): -        """ -        Enable the ADC for streaming -        """ -        self.log.trace("Setting up ADS54J56 for EISCAT operation...") -        self.regs.poke8(0x0011, 0x80) # Select Master page in Analog Bank -        self.regs.poke8(0x0053, 0x80) # Set clk divider to div-2 -        self.regs.poke8(0x0039, 0xC0) # ALWAYS WRITE 1 to this bit -        self.regs.poke8(0x0059, 0x20) # ALWAYS WRITE 1 to this bit -        self.regs.poke8(0x4004, 0x68) # Select Main Digital page -        self.regs.poke8(0x4003, 0x00) # -        self.regs.poke8(0x6042, 0x02) # Set interleaving correction to 3rd nyquist zone -        self.regs.poke8(0x604E, 0x80) # Enable correction -        self.regs.poke8(0x7042, 0x02) # Set interleaving correction to 3rd nyquist zone -        self.regs.poke8(0x704E, 0x80) # Enable correction -        self.regs.poke8(0x6000, 0x00) # Reset interleaving engine for Ch A-B (set to 0-1-0) -        self.regs.poke8(0x6000, 0x01) # -        self.regs.poke8(0x6000, 0x00) # -        self.regs.poke8(0x7000, 0x00) # Reset interleaving engine for Ch C-D (set to 0-1-0) -        self.regs.poke8(0x7000, 0x01) # -        self.regs.poke8(0x7000, 0x00) # -        self.regs.poke8(0x4004, 0x61) # Select decimation filter page of JESD bank. -        self.regs.poke8(0x4003, 0x41) # -        self.regs.poke8(0x6000, 0xE4) # DDC Mode 4 for A-B and E = CH A/B N value -        self.regs.poke8(0x7000, 0xE4) # DDC Mode 4 for A-B and E = CH A/B N value -        self.regs.poke8(0x6001, 0x04) # ALWAYS WRITE 1 to this bit -        self.regs.poke8(0x7001, 0x04) # ALWAYS WRITE 1 to this bit -        self.regs.poke8(0x6002, 0x0E) # Ch A/D N value -        self.regs.poke8(0x7002, 0x0E) # Ch A/D N value -        self.regs.poke8(0x4003, 0x00) # Select analog page in JESD Bank -        self.regs.poke8(0x4004, 0x6A) # -        self.regs.poke8(0x6016, 0x02) # PLL mode 40x for A-B -        self.regs.poke8(0x7016, 0x02) # PLL mode 40x for C-D -        self.regs.poke8(0x4003, 0x00) # Select digital page in JESD Bank -        self.regs.poke8(0x4004, 0x69) # -        self.regs.poke8(0x6000, 0xC0) # Enable JESD Mode control & set K for A-B -        self.regs.poke8(0x6001, 0x02) # Set JESD Mode to 40x for LMFS=2441 -        self.regs.poke8(0x7000, 0xC0) # Enable JESD Mode control & set K for C-D -        self.regs.poke8(0x7001, 0x02) # Set JESD Mode to 40x for LMFS=2441 -        self.regs.poke8(0x6006, 0x0F) # Set K to 16 -        self.regs.poke8(0x7006, 0x0F) # Set K to 16 -        # Choose the sync pin. We have both connected up to the FPGA, but we -        # can only use one at a time. Sync pins can become non-functional (e.g. -        # after ESD events) and thus we need the ability to choose between them. -        # In any case, we'll set the pin to issue a SYNC request for all 4 -        # channels. -        assert self.sync_line in ("AB", "CD") -        if self.sync_line == "AB": -            self.log.trace("Using SyncAB") -            self.regs.poke8(0x7001, 0x22) -        else: -            self.log.trace("Using SyncCD") -            self.regs.poke8(0x6001, 0x22) -        # This readback is pretty useless, but we use it as a debug mechanic to -        # see if anything is coming back from the chip: -        readback_test_addr = 0x11 -        readback_test_val = self.regs.peek8(readback_test_addr) -        self.log.trace("ADC readback reg 0x{:x} post-setup: 0x{:x}".format( -            readback_test_addr, -            readback_test_val, -        )) - - -class DboardClockControl(object): -    """ -    Control the FPGA MMCM for Radio Clock control. -    """ -    # Clocking Register address constants -    RADIO_CLK_MMCM      = 0x0020 -    PHASE_SHIFT_CONTROL = 0x0024 -    RADIO_CLK_ENABLES   = 0x0028 -    MGT_REF_CLK_STATUS  = 0x0030 - -    def __init__(self, regs, log): -        self.log = log -        self.regs = regs -        self.poke32 = self.regs.poke32 -        self.peek32 = self.regs.peek32 - -    def enable_outputs(self, enable=True): -        """ -        Enables or disables the MMCM outputs. -        """ -        with self.regs: -            if enable: -                self.poke32(self.RADIO_CLK_ENABLES, 0x011) -            else: -                self.poke32(self.RADIO_CLK_ENABLES, 0x000) - -    def reset_mmcm(self): -        """ -        Uninitialize and reset the MMCM -        """ -        self.log.trace("Disabling all Radio Clocks, then resetting MMCM...") -        self.enable_outputs(False) -        with self.regs: -            self.poke32(self.RADIO_CLK_MMCM, 0x1) - -    def enable_mmcm(self): -        """ -        Unreset MMCM and poll lock indicators - -        If MMCM is not locked after unreset, an exception is thrown. -        """ -        self.log.trace("Un-resetting MMCM...") -        with self.regs: -            self.poke32(self.RADIO_CLK_MMCM, 0x2) -            time.sleep(0.5) # Replace with poll and timeout TODO -            mmcm_locked = bool(self.peek32(self.RADIO_CLK_MMCM) & 0x10) -            if not mmcm_locked: -                self.log.error("MMCM not locked!") -                raise RuntimeError("MMCM not locked!") -            self.log.trace("Enabling output MMCM clocks...") -            self.enable_outputs(True) - -    def check_refclk(self): -        """ -        Not technically a clocking reg, but related. -        """ -        with self.regs: -            return bool(self.peek32(self.MGT_REF_CLK_STATUS) & 0x1) - - - -class JesdCoreEiscat(object): -    """ -    Wrapper for the JESD core. Note this core is specifically adapted for -    EISCAT, it is not general-purpose. -    """ -    # JESD Core Register Address Space Setup -    ADDR_BASE     = 0x2000 -    CORE_B_OFFSET = 0x1000 - -    # JESD Core Register Offsets -    JESD_SIGNATURE_REG = 0x100 -    JESD_REVISION_REG  = 0x104 - -    # Expected value for the JESD Core Signature -    CORE_ID_BASE = 0x4A455344 - -    def __init__(self, regs, slot_idx, core_idx, log): -        self.log = log -        self.regs = regs -        self.slot = "A" if slot_idx == 0 else "B" -        assert core_idx in (0, 1) -        self.core_idx = core_idx -        self.base_addr = self.ADDR_BASE + self.CORE_B_OFFSET * self.core_idx -        self.log.trace("Slot: {} JESD Core {}: Base address {:x}".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 -        with self.regs: -            core_id = self.peek32(self.JESD_SIGNATURE_REG) -            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(self.JESD_REVISION_REG) -            self.log.trace("Reading JESD date info: {:x}".format(date_info)) -        return True - -    def init(self): -        """ -        Run initialization sequence on JESD core. - -        Returns None, but will throw if there's a problem. -        """ -        self.log.trace("Init 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)) -        self._gt_polarity_control() - -    def init_deframer(self): -        """ -        Init FPGA JESD204B Deframer (RX) - -        Returns nothing, but throws on error. -        """ -        self.log.trace("Init JESD Deframer...") -        with self.regs: -            self.poke32(0x40, 0x02) # Force assertion of ADC SYNC -            self.poke32(0x50, 0x01) # Data = 0 = Scrambler enabled. Data = 1 = disabled. Must match ADC settings. -            if not self._gt_rx_reset(reset_only=False): -                raise RuntimeError("JESD Core did not come out of reset properly!") -            self.poke32(0x40, 0x00) # Stop forcing assertion of ADC SYNC - -    def check_deframer_status(self): -        """ -        Check deframer status (who would have thought) - -        Returns True if deframer status is good. -        """ -        deframer_status = self.peek32(0x40) & 0xFFFFFFFF -        if deframer_status != 0x3000001C: -            self.log.error("Unexpected JESD Core Deframer Status: {:x}".format(deframer_status)) -            return False -        return True - -    def _gt_pll_power_control(self): -        """ -        Power down unused CPLLs and QPLLs -        """ -        with self.regs: -            self.poke32(0x00C, 0xFFFC0000) -            self.log.trace("MGT power enabled readback: {:x}".format(self.peek32(0x00C))) - -    def _gt_rx_reset(self, reset_only=True): -        """ -        RX Reset. Either only puts it into reset, or also pulls it out of reset -        and makes sure lock status is correct. - -        Returns True on success. -        """ -        with self.regs: -            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 -                self.log.trace("MGT power enabled readback (rst seq): {:x}".format(self.peek32(0x00C))) -                self.log.trace("MGT CPLL lock readback (rst seq): {:x}".format(self.peek32(0x004))) -                lock_status = self.peek32(0x024) -                if lock_status & 0xFFFF0000 != 0x30000: -                    self.log.error( -                        "JESD Core {}: RX MGTs failed to reset! Status: 0x{:x}".format(self.core_idx, lock_status) -                    ) -                    return False -        return True - -    def _gt_pll_lock_control(self): -        """ -        Make sure PLLs are locked -        """ -        with self.regs: -            self.poke32(0x004, 0x11111111) # Reset CPLLs -            self.poke32(0x004, 0x11111100) # Unreset the ones we're using -            time.sleep(0.02) # TODO replace with poll and timeout -            self.poke32(0x010, 0x10000) # Clear all CPLL sticky bits -            self.log.trace("MGT CPLL lock readback (lock seq): {:x}".format(self.peek32(0x004))) -            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: 0x10}, -        }[self.slot][self.core_idx] -        self.log.trace( -            "JESD Core: Slot {}, ADC {}: Setting polarity control to 0x{:2x}".format( -                self.slot, self.core_idx, reg_val -            )) -        with self.regs: -            self.poke32(0x80, reg_val) - - -class EISCAT(DboardManagerBase): -    """ -    EISCAT Daughterboard -    """ - -    ######################################################################### -    # Overridables -    # -    # See DboardManagerBase for documentation on these fields -    ######################################################################### -    pids = [0x180] -    spi_chipselect = { -        "lmk": 0, -        "adc0": 1, -        "adc1": 2, -        "phase_dac": 3, -    } -    spi_factories = { -        "lmk": create_spidev_iface_sane, -        "adc0": create_spidev_iface_sane, -        "adc1": create_spidev_iface_sane, -        "phase_dac": create_spidev_iface_phasedac, -    } - - -    # Daughterboard Control Register address constants -    ADC_CONTROL    = 0x0600 -    LMK_STATUS     = 0x0604 -    DB_ENABLES     = 0x0608 -    DB_CH_ENABLES  = 0x060C -    SYSREF_CONTROL = 0x0620 - -    INIT_PHASE_DAC_WORD = 500 # Intentionally decimal -    PHASE_DAC_SPI_ADDR = 0x3 -    # External PPS pipeline delay from the PPS captured at the FPGA to TDC input, -    # in reference clock ticks -    EXT_PPS_DELAY = 3 -    # Variable PPS delay before the RP/SP pulsers begin. Fixed value for the N3xx devices. -    N3XX_INT_PPS_DELAY = 4 -    default_master_clock_rate = 104e6 -    default_time_source = 'external' -    default_current_jesd_rate = 2500e6 - -    def __init__(self, slot_idx, **kwargs): -        super(EISCAT, self).__init__(slot_idx, **kwargs) -        self.log = get_logger("EISCAT-{}".format(slot_idx)) -        self.log.trace("Initializing EISCAT daughterboard, slot index {}".format(self.slot_idx)) -        self.initialized = False -        self.ref_clock_freq = 10e6 # This is the only supported clock rate -        self.master_clock_rate = None -        # Define some attributes so that PyLint stays quiet: -        self.radio_regs = None -        self.jesd_cores = None -        self.lmk = None -        self.adcs = [] -        self.dboard_clk_control = None -        self.clock_synchronizer = None -        self._spi_ifaces = None - -    def is_initialized(self): -        """ -        Returns True if the daughterboard is a usable state and ready to stream -        """ -        return self.initialized - -    def init(self, args): -        """ -        Execute necessary actions to bring up the daughterboard: -        - Initializes all the software controls for all the chips and registers -        - Turns on the power -        - Initializes clocking -        - Synchronizes clocks to reference -        - Forwards PPS to the radio block - -        This assumes that an appropriate overlay was loaded. If not, this will -        fail loudly complaining about missing devices. - -        For operation (streaming), the ADCs and deframers still need to be -        initialized. See init_jesd_core_reset_adcs(), init_adcs_and_deframers(), -        and check_deframer_status(). - -        Note that this function will do nothing if the device was previously -        initialized, unless force_init was specified in the init args. -        """ -        def _init_dboard_regs(): -            " Create a UIO object to talk to dboard regs " -            self.log.trace("Getting uio...") -            return UIO( -                label="dboard-regs-{}".format(self.slot_idx), -                read_only=False -            ) -        def _init_jesd_cores(dboard_regs, slot_idx): -            " Init abstraction layer for JESD cores. Will also test registers. " -            return [ -                JesdCoreEiscat( -                    dboard_regs, -                    slot_idx, -                    core_idx, -                    self.log -                ) for core_idx in range(2) -            ] -        def _init_spi_devices(): -            " Returns abstraction layers to all the SPI devices " -            self.log.trace("Loading SPI interfaces...") -            return { -                key: self.spi_factories[key](self._spi_nodes[key]) -                for key in self._spi_nodes -            } -        def _init_clock_control(dboard_regs): -            " Create a dboard clock control object and reset it " -            dboard_clk_control = DboardClockControl(dboard_regs, self.log) -            dboard_clk_control.reset_mmcm() -            return dboard_clk_control -        def _init_lmk(slot_idx, lmk_spi, ref_clk_freq, -                      pdac_spi, init_phase_dac_word): -            """ -            Sets the phase DAC to initial value, and then brings up the LMK -            according to the selected ref clock frequency. -            Will throw if something fails. -            """ -            self.log.trace("Initializing Phase DAC to d{}.".format( -                init_phase_dac_word -            )) -            pdac_spi.poke16(0x3, init_phase_dac_word) -            return LMK04828EISCAT(lmk_spi, ref_clk_freq, slot_idx, self.log) -        def _sync_db_clock(): -            " Synchronizes the DB clock to the common reference " -            reg_offset = 0x200 -            ext_pps_delay = self.EXT_PPS_DELAY -            #from outdated inst of ClockSync -            #2.496e9,     # lmk_vco_freq -            synchronizer = ClockSynchronizer( -                self.radio_regs, -                self.lmk, -                self._spi_ifaces['phase_dac'], -                reg_offset, -                self.master_clock_rate, -                self.ref_clock_freq, -                1.9E-12, # fine phase shift. TODO don't hardcode. This should live in the EEPROM -                self.INIT_PHASE_DAC_WORD, -                self.PHASE_DAC_SPI_ADDR, -                ext_pps_delay, -                self.N3XX_INT_PPS_DELAY, -                self.slot_idx) -            # The radio clock traces on the motherboard are 69 ps longer for Daughterboard B -            # than Daughterboard A. We want both of these clocks to align at the converters -            # on each board, so adjust the target value for DB B. This is an N3xx series -            # peculiarity and will not apply to other motherboards. -            trace_delay_offset = {0:  0.0e-0, -                                  1: 69.0e-12}[self.slot_idx] -            offset = synchronizer.run( -                num_meas=[512, 128], -                target_offset = trace_delay_offset) -            offset_error = abs(offset) -            if offset_error > 100e-12: -                self.log.error("Clock synchronizer measured an offset of {:.1f} ps!".format( -                    offset_error*1e12 -                )) -                raise RuntimeError("Clock synchronizer measured an offset of {:.1f} ps!".format( -                    offset_error*1e12 -                )) -            else: -                self.log.debug("Residual synchronization error: {:.1f} ps.".format( -                    offset_error*1e12 -                )) -            synchronizer = None -            self.log.debug("Clock Synchronization Complete!") -        # Go, go, go! -        if args.get("force_init", False): -            self.log.info("Forcing re-initialization of dboard.") -        self.initialized = args.get("force_init", self.initialized) -        if self.initialized: -            self.log.debug( -                "Dboard was previously initialized; skipping init. " \ -                "Specify force_init=1 to force initialization." -            ) -            return True -        self.log.debug("init() called with args `{}'".format( -            ",".join(['{}={}'.format(x, args[x]) for x in args]) -        )) -        self.radio_regs = _init_dboard_regs() -        self.jesd_cores = _init_jesd_cores(self.radio_regs, self.slot_idx) -        self.log.debug("Radio-register UIO object successfully generated!") -        self._spi_ifaces = _init_spi_devices() # Chips don't have power yet! -        self.log.debug("Loaded SPI interfaces!") -        self._init_power(self.radio_regs) # Now, we can talk to chips via SPI -        self.dboard_clk_control = _init_clock_control(self.radio_regs) -        self.ref_clock_freq = 10e6 # This is the only supported clock rate -        self.master_clock_rate = self.default_master_clock_rate -        self.lmk = _init_lmk( -            self.slot_idx, -            self._spi_ifaces['lmk'], -            self.ref_clock_freq, -            self._spi_ifaces['phase_dac'], -            self.INIT_PHASE_DAC_WORD, -        ) -        self.adcs = [ -            ADS54J56(self._spi_ifaces[spi_iface], self.log) -            for spi_iface in ('adc0', 'adc1') -        ] -        self.dboard_clk_control.enable_mmcm() -        self.log.debug("Clocking Configured Successfully!") -        # Synchronize DB Clocks -        _sync_db_clock() -        self.log.debug("Clocks Sync'd Successfully!") -        # Clocks and PPS are now fully active! -        return True - -    def send_sysref(self): -        """ -        Send a SYSREF from MPM. This is not possible to do in a timed -        fashion though. -        """ -        self.log.trace("Sending SYSREF via MPM...") -        with self.radio_regs: -            self.radio_regs.poke32(self.SYSREF_CONTROL, 0x0) -            self.radio_regs.poke32(self.SYSREF_CONTROL, 0x1) -            self.radio_regs.poke32(self.SYSREF_CONTROL, 0x0) - -    def init_jesd_core_reset_adcs(self): -        """ -        - Initializes JESD cores -        - Initializes and resets ADCs -        """ -        def _check_jesd_cores(db_clk_control, jesd_cores): -            " Checks clocks are enabled; init the JESD core; throw on failure. " -            if not db_clk_control.check_refclk(): -                self.log.error("JESD Cores not getting a MGT RefClk!") -                raise RuntimeError("JESD Cores not getting a MGT RefClk") -            for jesd_core in jesd_cores: -                jesd_core.init() -        if self.initialized: -            self.log.debug( -                "Dboard already initialized; skipping initialization " \ -                "of ADCs and JESD cores." -            ) -            return True -        _check_jesd_cores( -            self.dboard_clk_control, -            self.jesd_cores -        ) -        for adc in self.adcs: -            adc.reset() -        self.log.trace("ADC Reset Sequence Complete!") -        return True - -    def init_adcs_and_deframers(self): -        """ -        Initialize the ADCs and the JESD deframers. Assumption is that they were -        SYSREF'd before. -        """ -        if self.initialized: -            self.log.debug( -                "Dboard already initialized; skipping initialization " \ -                "of ADCs and JESD cores." -            ) -            return True -        for adc in self.adcs: -            adc.setup() -        self.log.debug("ADC Initialization Complete!") -        for jesd_core in self.jesd_cores: -            jesd_core.init_deframer() -        return True - -    def check_deframer_status(self): -        """ -        Checks the JESD deframer status. This is done after initialization and -        sending a second SYSREF pulse. - -        Calling this function is required to signal a completion of the -        initialization sequence. - -        Will throw on failure. -        """ -        if self.initialized: -            self.log.debug( -                "Dboard already initialized; assuming JESD deframer status " \ -                "is fine." -            ) -            return True - -        error = False -        self.log.trace("check deframer status of both jesd cores.") -        for jesd_idx, jesd_core in enumerate(self.jesd_cores): -            self.log.trace("check deframer status of jesd core {}.".format(jesd_idx)) -            if not jesd_core.check_deframer_status(): -                self.log.error("JESD204B Core {} Error: Failed to Link. " \ -                    "Don't ignore this, please tell someone!".format(jesd_idx) -                ) -                error = True -                self.adcs[jesd_idx].swap_sync_line() -        if error: -            return False - -        self.log.debug("JESD Core Initialized, link up! (woohoo!)") -        self.initialized = True -        return self.initialized - -    def shutdown(self): -        """ -        Safely turn off the daughterboard. This will take away power to the -        components; a re-initialization will be necessary after calling this. -        """ -        self.log.info("Shutting down daughterboard") -        self.initialized = False -        self._deinit_power(self.radio_regs) - -    def _init_power(self, regs): -        """ -        Turn on power to the dboard. - -        After this function, we should never touch this group again (other -        than turning it off, maybe). -        """ -        # Enable all channels first due to a signal integrity issue when enabling them -        # after the LNA enable is asserted. -        self.log.trace("Enabling power to the daughterboard...") -        with regs: -            regs.poke32(self.DB_CH_ENABLES, 0x000000FF) -            regs.poke32(self.DB_ENABLES,    0x01000000) -            regs.poke32(self.DB_ENABLES,    0x00010101) -            regs.poke32(self.ADC_CONTROL,   0x00010000) -        time.sleep(0.100) - -    def _deinit_power(self, regs): -        """ -        Turn off power to the dboard. Sequence is reverse of init_power. -        """ -        self.log.trace("Disabling power to the daughterboard...") -        with regs: -            regs.poke32(self.ADC_CONTROL,   0x00100000) -            regs.poke32(self.DB_ENABLES,    0x10101010) -            regs.poke32(self.DB_CH_ENABLES, 0x00000000) # Disable all channels (last) - -    def update_ref_clock_freq(self, freq): -        """ -        Call this to notify the daughterboard about a change in reference clock -        """ -        if freq != self.ref_clock_freq: -            self.log.error( -                "EISCAT daughterboard only supports a reference clock " \ -                "frequency of {} MHz".format(self.ref_clock_freq/1e6) -            ) -            raise RuntimeError("Invalid reference clock frequency: {}".format( -                freq -            )) - - diff --git a/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py b/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py deleted file mode 100644 index 509d65be0..000000000 --- a/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py +++ /dev/null @@ -1,220 +0,0 @@ -# -# Copyright 2017 Ettus Research, a National Instruments Company -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -""" -LMK04828 driver for use with Magnesium -""" - -import time -from usrp_mpm.chips import LMK04828 - -class LMK04828EISCAT(LMK04828): -    """ -    LMK04828 controls for EISCAT daughterboard -    """ -    def __init__(self, regs_iface, ref_clock_freq, slot=None, log=None): -        LMK04828.__init__(self, regs_iface, log) -        self.log.trace("Using reference clock frequency {} MHz".format(ref_clock_freq/1e6)) -        if ref_clock_freq != 10e6: -            error_msg = "Invalid reference clock frequency: {} MHz. " \ -                        "Must be 10 MHz.".format(ref_clock_freq) -            self.log.error(error_msg) -            raise RuntimeError(error_msg) -        self.ref_clock_freq = ref_clock_freq -        self.init() -        self.config() - - -    def get_vco_freq(self): -        """ -        Return the hard coded VCO frequency in the LMK PLL2. -        """ -        return 2.496e9 - -    def init(self): -        """ -        Basic init. Turns it on. Let's us read SPI. -        """ -        self.log.debug("Reset LMK & Verify") -        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("LMK Initialization") -        clkin0_r_divider = {10e6: 0x0A, 20e6: 0x14}[self.ref_clock_freq] -        self.pokes8(( -            (0x100, 0x6C), # CLKout Config -            (0x101, 0x66), # CLKout Config -            (0x102, 0x66), # 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, 0x67), # CLKout Config -            (0x10A, 0x67), # CLKout Config -            (0x10B, 0x00), # CLKout Config -            (0x10C, 0x20), # CLKout Config -            (0x10D, 0x00), # CLKout Config -            (0x10E, 0x71), # CLKout Config -            (0x10F, 0x05), # CLKout Config -            (0x110, 0x6C), # CLKout Config -            (0x111, 0x67), # CLKout Config -            (0x112, 0x67), # CLKout Config -            (0x113, 0x00), # CLKout Config -            (0x114, 0x20), # CLKout Config -            (0x115, 0x00), # CLKout Config -            (0x116, 0x71), # CLKout Config -            (0x117, 0x05), # CLKout Config -            (0x118, 0x6C), # CLKout Config -            (0x119, 0x67), # CLKout Config -            (0x11A, 0x67), # CLKout Config -            (0x11B, 0x00), # CLKout Config -            (0x11C, 0x20), # CLKout Config -            (0x11D, 0x00), # CLKout Config -            (0x11E, 0x71), # CLKout Config -            (0x11F, 0x05), # CLKout Config -            (0x120, 0x78), # CLKout Config -            (0x121, 0x66), # CLKout Config -            (0x122, 0x66), # 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 -            (0x12A, 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, 0x67), # CLKout Config -            (0x132, 0x67), # CLKout Config -            (0x133, 0x00), # CLKout Config -            (0x134, 0x20), # CLKout Config -            (0x135, 0x00), # CLKout Config -            (0x136, 0x71), # CLKout Config -            (0x137, 0x01), # 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, 0x08), # 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, 0x00), # Holdover Settings; bits 0/1 = '0' per long PLL1 lock time debug -            (0x151, 0x02), # Holdover Settings (defaults) -            (0x152, 0x00), # Holdover Settings (defaults) -            (0x153, 0x00), # CLKin0_R divider [13:8], default = 0 -            (0x154, clkin0_r_divider), # 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, 0x27), # 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 -        )) -        # TODO: change to Polling. -        time.sleep(1.0) # Increased time to wait for DAC and VCXO to settle. -        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) -        if not self.check_plls_locked(): -            raise RuntimeError("At least one LMK PLL did not lock! Check the logs for details.") -        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.debug("LMK init'd and locked!") - -    def lmk_shift(self, num_shifts=0): -        """ -        Apply time shift - -        TODO: See if we can move this up to parent class -        """ -        ddly_value = 0x67 if num_shifts >= 0 else 0x65 -        self.pokes8(( -            (0x141, 0x4E), # Dynamic digital delay enable -            (0x143, 0x53), # SYSREF_CLR; SYNC Enabled; SYNC from pulser @ regwrite -            (0x139, 0x02), # SYSREF_MUX = Pulser -            (0x109, ddly_value), # Set DDLY values for DCLKout2 +/-1 on low cnt. -                                 # To Increment phase, write 0x65. Decrement = 0x67 -            (0x10A, ddly_value), # Hidden register. Write the same as previous based on inc/dec. -            (0x111, ddly_value), # Set DDLY values for DCLKout4 +/-1 on low cnt -            (0x112, ddly_value), # Hidden register. Write the same as previous based on inc/dec. -            (0x119, ddly_value), # Set DDLY values for DCLKout6 +/-1 on low cnt -            (0x11A, ddly_value), # Hidden register. Write the same as previous based on inc/dec. -            (0x131, ddly_value), # Set DDLY values for DCLKout12 +/-1 on low cnt -            (0x132, ddly_value), # Hidden register. Write the same as previous based on inc/dec. -            (0x144, 0xB1), # Enable SYNC on outputs 2,4,6,12 -        )) -        for x in range(abs(num_shifts)): -            self.poke8(0x142, 0x1) -        self.poke8(0x144, 0xFF) # Disable SYNC on all outputs - diff --git a/mpm/python/usrp_mpm/dboard_manager/unknown.py b/mpm/python/usrp_mpm/dboard_manager/unknown.py index 7afc66f74..b38346af7 100644 --- a/mpm/python/usrp_mpm/dboard_manager/unknown.py +++ b/mpm/python/usrp_mpm/dboard_manager/unknown.py @@ -4,7 +4,7 @@  # SPDX-License-Identifier: GPL-3.0-or-later  #  """ -EISCAT rx board implementation module +Dummy rx board implementation module  """  from usrp_mpm.dboard_manager import DboardManagerBase diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx.py b/mpm/python/usrp_mpm/periph_manager/n3xx.py index 09386106d..2fbb12365 100644 --- a/mpm/python/usrp_mpm/periph_manager/n3xx.py +++ b/mpm/python/usrp_mpm/periph_manager/n3xx.py @@ -28,7 +28,6 @@ from usrp_mpm.periph_manager.n3xx_periphs import BackpanelGPIO  from usrp_mpm.periph_manager.n3xx_periphs import MboardRegsControl  from usrp_mpm.periph_manager.n3xx_periphs import RetimerQSFP  from usrp_mpm.dboard_manager.magnesium import Magnesium -from usrp_mpm.dboard_manager.eiscat import EISCAT  from usrp_mpm.dboard_manager.rhodium import Rhodium  N3XX_DEFAULT_EXT_CLOCK_FREQ = 10e6 @@ -49,7 +48,6 @@ N3XX_FPGPIO_WIDTH = 12  # Import daughterboard PIDs from their respective classes  MG_PID = Magnesium.pids[0] -EISCAT_PID = EISCAT.pids[0]  RHODIUM_PID = Rhodium.pids[0]  ############################################################################### @@ -109,7 +107,6 @@ class n3xx(ZynqComponents, PeriphManagerBase):                                              # still use the n310.bin image.                                              # We'll leave this here for                                              # debugging purposes. -        ('n310', (EISCAT_PID , EISCAT_PID )): 'eiscat',          ('n310', (RHODIUM_PID, RHODIUM_PID)): 'n320',          ('n310', (RHODIUM_PID,            )): 'n320',      }  | 
