From fad36514e56c2da459637b5abe261033e40fa8fd Mon Sep 17 00:00:00 2001
From: Mark Meserve <mark.meserve@ni.com>
Date: Wed, 17 Oct 2018 15:46:41 -0500
Subject: nijesdcore: add variable configuration support

---
 mpm/python/usrp_mpm/cores/nijesdcore.py         | 66 +++++++++++++++----------
 mpm/python/usrp_mpm/cores/tdc_sync.py           |  4 +-
 mpm/python/usrp_mpm/dboard_manager/magnesium.py |  3 +-
 mpm/python/usrp_mpm/dboard_manager/mg_init.py   |  7 ++-
 4 files changed, 50 insertions(+), 30 deletions(-)

diff --git a/mpm/python/usrp_mpm/cores/nijesdcore.py b/mpm/python/usrp_mpm/cores/nijesdcore.py
index d6f465c53..90b45430f 100644
--- a/mpm/python/usrp_mpm/cores/nijesdcore.py
+++ b/mpm/python/usrp_mpm/cores/nijesdcore.py
@@ -1,5 +1,5 @@
 #
-# Copyright 2017 Ettus Research, a National Instruments Company
+# Copyright 2017-2018 Ettus Research, a National Instruments Company
 #
 # SPDX-License-Identifier: GPL-3.0-or-later
 #
@@ -8,11 +8,12 @@ JESD FPGA Core Interface
 """
 
 import time
+from six import iteritems
 from builtins import hex
 from builtins import object
 from usrp_mpm.mpmlog import get_logger
 
-class NIMgJESDCore(object):
+class NIJESDCore(object):
     """
     Provide interface for the FPGA JESD Core.
     Works with Magnesium/Mykonos daughterboards only.
@@ -21,6 +22,13 @@ class NIMgJESDCore(object):
     regs -- regs class to use for peek/poke
     """
 
+    # Bump this whenever we stop supporting older FPGA images or boards.
+    # YYMMDDHH
+    OLDEST_COMPAT_VERSION = 0x17122214
+    # Bump this whenever changes are made to the host code.
+    CURRENT_VERSION = 0x18071209
+
+    # Register offsets for JESD core.
     DB_ID                      = 0x0630
     MGT_QPLL_CONTROL           = 0x2000
     MGT_PLL_POWER_DOWN_CONTROL = 0x200C
@@ -38,31 +46,37 @@ class NIMgJESDCore(object):
     JESD_REVISION_REG          = 0x2104
     JESD_OLD_COMPAT_REV_REG    = 0x2108
 
+    # NIJESDCore configuration attributes.
+    # These parameters should be set per board (ie. Mg, Rh, etc.) at the creation
+    # of the NIJESDCore object. Use **kwargs to pass a dict which keys correspond
+    # to the parameter's name (eg. {"lmfc_divider": 12, ...}).
+    JESDCORE_DEFAULTS = {"qplls_used"        : 1,       # Number of QPLLs used.
+                         "cplls_used"        : 0,       # Number of CPLLs used.
+                         "rx_lanes"          : 4,       # Number of RX lanes used.
+                         "tx_lanes"          : 4,       # Number of TX lanes used.
+                         "bypass_descrambler": True,    # Bypass the desrambler IP.
+                         "bypass_scrambler"  : True,    # Bypass the scrambler IP.
+                         "lmfc_divider"      : 10,      # Number of FPGA clock cycles per LMFC period.
+                         "rx_sysref_delay"   : 10,      # Cycles of delay added to RX SYSREF
+                         "tx_sysref_delay"   : 10,      # Cycles of delay added to TX SYSREF
+                         "tx_driver_swing"   : 0b1111,  # See UG476, TXDIFFCTRL
+                         "tx_precursor"      : 0b00000, # See UG476, TXPRECURSOR
+                         "tx_postcursor"     : 0b00000} # See UG476, TXPOSTCURSOR
 
-    def __init__(self, regs, slot_idx=0):
+    def __init__(self, regs, slot_idx=0, **kwargs):
         self.regs = regs
         self.log = get_logger("NIJESD204bCore-{}".format(slot_idx))
         assert hasattr(self.regs, 'peek32')
         assert hasattr(self.regs, 'poke32')
-        # FutureWork: The following are constants for the Magnesium board. These need
-        # to change to variables to support other interfaces.
-        self.qplls_used = 1
-        self.cplls_used = 0
-        self.rx_lanes = 4
-        self.tx_lanes = 4
-        self.bypass_descrambler = False
-        self.bypass_scrambler = True
-        self.lmfc_divider = 20 # Number of FPGA clock cycles per LMFC period.
-        self.rx_sysref_delay = 8  # Cycles of delay added to RX SYSREF
-        self.tx_sysref_delay = 11 # Cycles of delay added to TX SYSREF
-        self.tx_driver_swing = 0b1111 # See UG476, TXDIFFCTRL
-        self.tx_precursor = 0b00000 # See UG476, TXPRECURSOR
-        self.tx_postcursor = 0b00000 # See UG476, TXPOSTCURSOR
-        # Bump this whenever we stop supporting older FPGA images or boards.
-        # YYMMDDHH
-        self.oldest_compat_version = 0x17122214
-        # Bump this whenever changes are made to the host code.
-        self.current_version = 0x17122214
+        # Initialize the driver's attributes with the default value, or a user-given
+        # value if the attribute key exists in kwargs.
+        for key, default_value in iteritems(self.JESDCORE_DEFAULTS):
+            setattr(self, key, kwargs.get(key, default_value))
+            assert type(getattr(self, key)) == type(default_value), \
+                "Invalid type for attribute {}".format(key)
+            self.log.trace("Initialized attribute {0} = {1}."
+                           .format(key, getattr(self, key)))
+        
 
     def check_core(self):
         """
@@ -76,15 +90,15 @@ class NIMgJESDCore(object):
         #   Host Current Rev >= FPGA Oldest Compatible Rev
         fpga_current_revision    = self.regs.peek32(self.JESD_REVISION_REG) & 0xFFFFFFFF
         fpga_old_compat_revision = self.regs.peek32(self.JESD_OLD_COMPAT_REV_REG) & 0xFFFFFFFF
-        if fpga_current_revision < self.oldest_compat_version:
+        if fpga_current_revision < self.OLDEST_COMPAT_VERSION:
             self.log.error("Revision check failed! MPM oldest supported revision "
                            "(0x{:08X}) is too new for this FPGA revision (0x{:08X})."
-                           .format(self.oldest_compat_version, fpga_current_revision))
+                           .format(self.OLDEST_COMPAT_VERSION, fpga_current_revision))
             raise RuntimeError('This MPM version does not support the loaded FPGA image. Please update images!')
-        if self.current_version < fpga_old_compat_revision:
+        if self.CURRENT_VERSION < fpga_old_compat_revision:
             self.log.error("Revision check failed! FPGA oldest compatible revision "
                            "(0x{:08X}) is too new for this MPM version (0x{:08X})."
-                           .format(fpga_current_revision, self.current_version))
+                           .format(fpga_current_revision, self.CURRENT_VERSION))
             raise RuntimeError('The loaded FPGA version is too new for MPM. Please update MPM!')
         self.log.trace("JESD Core current revision: 0x{:08X}".format(fpga_current_revision))
         self.log.trace("JESD Core oldest compatible revision: 0x{:08X}".format(fpga_old_compat_revision))
diff --git a/mpm/python/usrp_mpm/cores/tdc_sync.py b/mpm/python/usrp_mpm/cores/tdc_sync.py
index bb94d9826..f7ed09b7d 100644
--- a/mpm/python/usrp_mpm/cores/tdc_sync.py
+++ b/mpm/python/usrp_mpm/cores/tdc_sync.py
@@ -1,5 +1,5 @@
 #
-# Copyright 2017 Ettus Research, a National Instruments Company
+# Copyright 2017-2018 Ettus Research, a National Instruments Company
 #
 # SPDX-License-Identifier: GPL-3.0-or-later
 #
@@ -109,7 +109,7 @@ class ClockSynchronizer(object):
                                self.ref_clk_freq*1e-6))
             raise RuntimeError("TDC does not support the selected reference clock rate!")
         self.supported_radio_clk_freqs = [104e6, 122.88e6, 125e6, 153.6e6, 156.25e6, \
-                                          200e6, 250e6]
+                                          200e6, 245.76e6, 250e6]
         if self.radio_clk_freq not in self.supported_radio_clk_freqs:
             self.log.error("Clock synchronizer does not support the selected radio clock"
                            " frequency. Selected rate: {:.2f} MHz".format(
diff --git a/mpm/python/usrp_mpm/dboard_manager/magnesium.py b/mpm/python/usrp_mpm/dboard_manager/magnesium.py
index 06dff175f..ca3c74e4b 100644
--- a/mpm/python/usrp_mpm/dboard_manager/magnesium.py
+++ b/mpm/python/usrp_mpm/dboard_manager/magnesium.py
@@ -422,7 +422,8 @@ class Magnesium(DboardManagerBase):
             db_clk_control = DboardClockControl(dboard_ctrl_regs, self.log)
             db_clk_control.reset_mmcm()
             # Place the JESD204b core in reset, mainly to reset QPLL/CPLLs.
-            jesdcore = nijesdcore.NIMgJESDCore(dboard_ctrl_regs, self.slot_idx)
+            jesdcore = nijesdcore.NIJESDCore(dboard_ctrl_regs, self.slot_idx,
+                                             **MagnesiumInitManager.JESD_DEFAULT_ARGS)
             jesdcore.reset()
             # The reference clock is handled elsewhere since it is a motherboard-
             # level clock.
diff --git a/mpm/python/usrp_mpm/dboard_manager/mg_init.py b/mpm/python/usrp_mpm/dboard_manager/mg_init.py
index 62cc27a4f..d2b597c21 100644
--- a/mpm/python/usrp_mpm/dboard_manager/mg_init.py
+++ b/mpm/python/usrp_mpm/dboard_manager/mg_init.py
@@ -76,6 +76,11 @@ class MagnesiumInitManager(object):
     # Variable PPS delay before the RP/SP pulsers begin. Fixed value for the
     # N3xx devices.
     N3XX_INT_PPS_DELAY = 4
+    # JESD core default configuration.
+    JESD_DEFAULT_ARGS = {"bypass_descrambler": False,
+                         "lmfc_divider"      : 20,
+                         "rx_sysref_delay"   : 8,
+                         "tx_sysref_delay"   : 11}
 
     def __init__(self, mg_class, spi_ifaces):
         self.mg_class = mg_class
@@ -525,7 +530,7 @@ class MagnesiumInitManager(object):
             read_only=False
         ) as dboard_ctrl_regs:
             self.log.trace("Creating jesdcore object...")
-            jesdcore = nijesdcore.NIMgJESDCore(dboard_ctrl_regs, slot_idx)
+            jesdcore = nijesdcore.NIJESDCore(dboard_ctrl_regs, slot_idx, **self.JESD_DEFAULT_ARGS)
             # Now get cracking with the actual init sequence:
             self.log.trace("Creating dboard clock control object...")
             db_clk_control = DboardClockControl(dboard_ctrl_regs, self.log)
-- 
cgit v1.2.3