mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
144 lines
5.2 KiB
Python
144 lines
5.2 KiB
Python
#
|
|
# 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!")
|
|
|