aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fpga/usrp3/top/n3xx/setupenv.sh1
-rw-r--r--host/lib/usrp/dboard/CMakeLists.txt3
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt2
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/__init__.py1
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/eiscat.py731
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py220
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/unknown.py2
-rw-r--r--mpm/python/usrp_mpm/periph_manager/n3xx.py3
8 files changed, 1 insertions, 962 deletions
diff --git a/fpga/usrp3/top/n3xx/setupenv.sh b/fpga/usrp3/top/n3xx/setupenv.sh
index bb20a330c..d3057d5ce 100644
--- a/fpga/usrp3/top/n3xx/setupenv.sh
+++ b/fpga/usrp3/top/n3xx/setupenv.sh
@@ -8,7 +8,6 @@ declare -A PRODUCT_ID_MAP
PRODUCT_ID_MAP["N300"]="zynq/xc7z035/ffg900/-2"
PRODUCT_ID_MAP["N310"]="zynq/xc7z100/ffg900/-2"
PRODUCT_ID_MAP["N320"]="zynq/xc7z100/ffg900/-2"
-PRODUCT_ID_MAP["EISCAT"]="zynq/xc7z100/ffg900/-2"
# Set default part for simulation
export ARCH=zynq
diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt
index 1d31d6930..9461cfce0 100644
--- a/host/lib/usrp/dboard/CMakeLists.txt
+++ b/host/lib/usrp/dboard/CMakeLists.txt
@@ -49,9 +49,6 @@ endif(ENABLE_N300)
if(ENABLE_N320)
INCLUDE_SUBDIRECTORY(rhodium)
endif(ENABLE_N320)
-if(ENABLE_MPMD AND ENABLE_EISCAT)
- INCLUDE_SUBDIRECTORY(eiscat)
-endif(ENABLE_MPMD AND ENABLE_EISCAT)
if(ENABLE_X400)
INCLUDE_SUBDIRECTORY(zbx)
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',
}