diff options
author | Martin Braun <martin.braun@ettus.com> | 2017-04-19 18:44:03 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:03:45 -0800 |
commit | b05f72f339dcb02cc6efc6f2bc7d92c4476b5cc9 (patch) | |
tree | 1e08eed1582ac6e7ba119edfd725d7e1d097adc1 | |
parent | f2f1204ea47865cf16e2a27fb3e004baedbb82f8 (diff) | |
download | uhd-b05f72f339dcb02cc6efc6f2bc7d92c4476b5cc9.tar.gz uhd-b05f72f339dcb02cc6efc6f2bc7d92c4476b5cc9.tar.bz2 uhd-b05f72f339dcb02cc6efc6f2bc7d92c4476b5cc9.zip |
mpm: Added NI JESD core controller
-rw-r--r-- | mpm/python/usrp_mpm/nijesdcore.py | 144 |
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!") + |