aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2017-04-19 18:44:03 -0700
committerMartin Braun <martin.braun@ettus.com>2017-12-22 15:03:45 -0800
commitb05f72f339dcb02cc6efc6f2bc7d92c4476b5cc9 (patch)
tree1e08eed1582ac6e7ba119edfd725d7e1d097adc1
parentf2f1204ea47865cf16e2a27fb3e004baedbb82f8 (diff)
downloaduhd-b05f72f339dcb02cc6efc6f2bc7d92c4476b5cc9.tar.gz
uhd-b05f72f339dcb02cc6efc6f2bc7d92c4476b5cc9.tar.bz2
uhd-b05f72f339dcb02cc6efc6f2bc7d92c4476b5cc9.zip
mpm: Added NI JESD core controller
-rw-r--r--mpm/python/usrp_mpm/nijesdcore.py144
1 files changed, 144 insertions, 0 deletions
diff --git a/mpm/python/usrp_mpm/nijesdcore.py b/mpm/python/usrp_mpm/nijesdcore.py
new file mode 100644
index 000000000..60888a5c1
--- /dev/null
+++ b/mpm/python/usrp_mpm/nijesdcore.py
@@ -0,0 +1,144 @@
+#
+# 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/>.
+#
+"""
+JESD FPGA Core Interface
+"""
+
+import time
+from .mpmlog import get_logger
+
+class NIMgJESDCore(object):
+ """
+ Provide interface for the FPGA JESD Core.
+ Works with Magnesium/Mykonos daughterboards only.
+
+ Arguments:
+ regs -- regs class to use for peek/poke
+ """
+ def __init__(self, regs, side=None):
+ side = side or "-0"
+ self.regs = regs
+ self.log = get_logger('NIMgJESDCore'+side)
+ assert hasattr(self.regs, 'peek32')
+ assert hasattr(self.regs, 'poke32')
+
+ def unreset_mmcm(self):
+ # new_val = self.regs.peek32(0x0) & ~0x8
+ # self.log.trace("Unresetting MMCM, writing value {:X}".format(new_val))
+ self.regs.poke32(0x0, 0x7)
+
+ def check_core(self):
+ """
+ Verify JESD core returns correct ID
+ """
+ self.log.trace("Checking JESD Core...")
+ if self.regs.peek32(0x2100) != 0x4A455344:
+ raise Exception('JESD Core signature mismatch! Check that core is mapped correctly')
+ #if self.regs.peek32(0x2104) != 0xFF
+ #error here for date revision mismatch
+ return True
+
+ def init_deframer(self):
+ " Initialize deframer "
+ self.log.trace("Initializing deframer...")
+ self.regs.poke32(0x2040, 0x2)
+ self.regs.poke32(0x2050, 0x0)
+ self._gt_reset('rx', reset_only=False)
+ self.regs.poke32(0x2040, 0x0)
+
+ def init_framer(self):
+ " Initialize framer "
+ self.log.trace("Initializing framer...")
+ self.regs.poke32(0x2060, 0x2002)
+ self._gt_reset('tx', reset_only=False)
+ self.regs.poke32(0x2064, 0xF0000)
+ time.sleep(0.001)
+ self.regs.poke32(0x2068, 0x1)
+ rb = self.regs.peek32(0x2060)
+ if rb & 0x100 != 0x100:
+ raise Exception('TX core is not idle after reset')
+ self.regs.poke32(0x2060, 0x1001)
+
+ def get_framer_status(self):
+ " Return True if framer is in good status "
+ rb = self.regs.peek32(0x2060)
+ self.log.trace("Returning framer status: {0}".format(hex(rb & 0xFF0)))
+ return rb & 0xFF0 == 0x6C0
+
+ def get_deframer_status(self):
+ " Return True if deframer is in good status "
+ rb = self.regs.peek32(0x2040)
+ self.log.trace("Returning deframer status: {0}".format(hex(rb & 0xFFFFFFFF)))
+ return rb & 0xFFFFFFFF == 0xF000001C
+
+ def init(self):
+ """
+ Initializes to the core. Needs to happen after the clock signal is ready.
+ """
+ self.log.trace("Initializing core...")
+ self._gt_pll_power_control()
+ self._gt_reset('tx', reset_only=True)
+ self._gt_reset('rx', reset_only=True)
+ self._gt_pll_lock_control()
+
+ def send_sysref_pulse(self):
+ """
+ Toggles the LMK pin that triggers a SYSREF pulse.
+ Note: SYSREFs must be enabled on LMK separately beforehand.
+ """
+ self.log.trace("Sending SYSREF pulse...")
+ self.regs.poke32(0x206C, 0x40000000) # Bit 30. Self-clears.
+
+ def _gt_reset(self, tx_or_rx, reset_only=False):
+ " Put MGTs into reset. Optionally unresets and enables them "
+ assert tx_or_rx.lower() in ('rx', 'tx')
+ mgt_reg = {'tx': 0x2020, 'rx': 0x2024}[tx_or_rx]
+ self.log.trace("Resetting TX MGTs...")
+ self.regs.poke32(mgt_reg, 0x10)
+ if not reset_only:
+ self.regs.poke32(mgt_reg, 0x20)
+ rb = -1
+ for _ in range(20):
+ rb = self.regs.peek32(mgt_reg)
+ if rb & 0xFFFF0000 == 0x000F0000:
+ return True
+ time.sleep(0.01)
+ raise Exception('Timeout in GT {trx} Reset (Readback: 0x{rb:X})'.format(
+ trx=tx_or_rx.upper(),
+ rb=(rb & 0xFFFF0000),
+ ))
+ return True
+
+ def _gt_pll_power_control(self):
+ " Power down unused CPLLs and QPLLs "
+ self.log.trace("Powering down unused CPLLs and QPLLs...")
+ self.regs.poke32(0x200C, 0xFFF000E)
+
+ def _gt_pll_lock_control(self):
+ """
+ Turn on the PLLs we're using, and make sure lock bits are set.
+ """
+ self.regs.poke32(0x2000, 0x1111) # Reset QPLLs
+ self.regs.poke32(0x2000, 0x1110) # Unreset the ones we're using
+ time.sleep(0.002) # alternatively, poll on the locked bit below
+ self.regs.poke32(0x2000, 0x10000) # Clear all QPLL sticky bits
+ rb = self.regs.peek32(0x2000) # Read QPLL locked and no unlocked stickies.
+ self.log.trace("Reading QPLL lock bit: {0}".format(hex(rb & 0xF)))
+ # Error: GT PLL failed to lock.
+ if rb & 0xF != 0x2:
+ raise Exception("GT PLL failed to lock!")
+