mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
This enables the inv_sinc_ab and inv_sinc_cd flags for the DAC, turning on the inverse sinc filter.
342 lines
19 KiB
Python
342 lines
19 KiB
Python
#
|
|
# Copyright 2018 Ettus Research, a National Instruments Company
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
#
|
|
"""
|
|
DAC37J82 driver for use with Rhodium
|
|
"""
|
|
|
|
import time
|
|
from builtins import object
|
|
from ..mpmlog import get_logger
|
|
|
|
class DAC37J82Rh(object):
|
|
"""
|
|
This class provides an interface to configure the DAC37J82 IC through SPI.
|
|
"""
|
|
|
|
DAC_VENDOR_ID = 0b01
|
|
DAC_VERSION_ID = 0b010 # Version used in Rhodium Rev. A
|
|
|
|
def __init__(self, slot_idx, regs_iface, parent_log=None):
|
|
self.log = parent_log.getChild("DAC37J82") if parent_log is not None \
|
|
else get_logger("DAC37J82-{}".format(slot_idx))
|
|
self.slot_idx = slot_idx
|
|
self.regs = regs_iface
|
|
assert hasattr(self.regs, 'peek16')
|
|
assert hasattr(self.regs, 'poke16')
|
|
|
|
def _verify_chip_id():
|
|
chip_id = self.regs.peek16(0x7F) & 0x001F
|
|
self.log.trace("DAC Vendor & Version ID: 0x{:X}".format(chip_id))
|
|
if chip_id != ((self.DAC_VENDOR_ID << 3) | self.DAC_VERSION_ID):
|
|
self.log.error("Wrong Vendor & Version 0x{:X}".format(chip_id))
|
|
return False
|
|
return True
|
|
|
|
self.reset()
|
|
|
|
if not _verify_chip_id():
|
|
raise RuntimeError("Unable to locate DAC37J82")
|
|
|
|
# Define variable configuration per slot.
|
|
# The JESD lanes going to the DAC pins are swapped differently:
|
|
# DBA: 0 -> 0 / 1 -> 1 / 2 -> 2 / 3 -> 3
|
|
# DBB: 0 -> 0 / 1 -> 1 / 2 -> 3 / 3 -> 2
|
|
# Therefore, depending on the DB that is being configured, we need
|
|
# to change the JESD lanes internal routing in the DAC to compensate
|
|
# for the board traces swapping.
|
|
self.lanes_ids_1 = {0: 0x0044, 1: 0x0046}[self.slot_idx] # config70
|
|
self.lanes_ids_2 = {0: 0x190A, 1: 0x110A}[self.slot_idx] # config71
|
|
self.octetpath_sel = {0: 0x0123, 1: 0x0132}[self.slot_idx] # config95
|
|
|
|
self.init()
|
|
|
|
|
|
def tx_enable(self, enable=False):
|
|
"""
|
|
Enable/disable the analog TX output.
|
|
"""
|
|
enable_bit = 0b1 if enable else 0b0
|
|
prev_val = self.regs.peek16(0x03)
|
|
self.regs.poke16(0x03, prev_val | enable_bit)
|
|
|
|
|
|
def pokes16(self, addr_vals):
|
|
"""
|
|
Apply a series of pokes.
|
|
pokes16((0,1),(0,2)) is the same as calling poke16(0,1), poke16(0,2).
|
|
"""
|
|
for addr, val in addr_vals:
|
|
self.regs.poke16(addr, val)
|
|
|
|
|
|
def init(self):
|
|
"""
|
|
Basic init that disables the analog output.
|
|
"""
|
|
self.tx_enable(False) # Set TXENABLE low at the DAC
|
|
self.log.trace("DAC's Analog TX output is disabled.")
|
|
|
|
def reset(self):
|
|
"""
|
|
Reset the DAC state
|
|
"""
|
|
self.regs.poke16(0x02, 0x2002) # Deassert the reset for the SIF registers
|
|
self.regs.poke16(0x02, 0x2003) # Assert the reset for the SIF registers
|
|
|
|
def config(self):
|
|
"""
|
|
Check the clock status, and write configuration values!
|
|
"""
|
|
def _check_pll_lock():
|
|
pll_ool_alarms = self.regs.peek16(0x6C)
|
|
if (pll_ool_alarms & 0x0008) != 0x0000:
|
|
self.log.warning("PLL reporting unlocked... Status: 0x{:x}"
|
|
.format(pll_ool_alarms))
|
|
return False
|
|
return True
|
|
|
|
self.log.trace("Reset DAC & Clear alarm bits")
|
|
self.reset()
|
|
self.regs.poke16(0x6C, 0x0000) # Clear alarm bits for PLLs
|
|
|
|
self.log.trace("DAC Configuration.")
|
|
self.pokes16((
|
|
(0x00, 0x001B), # config0: Interpolation 1x; ALARM enabled w/ pos. logic.
|
|
(0x01, 0x0003), # config1: Rewriting reserved default values.
|
|
(0x02, 0x0002), # config2: Data not zero when link not established; 2's comp. arrives at input.
|
|
(0x03, 0x9300), # config3: Coarse DAC = 10 (9+1); TXENABLE internal is kept low.
|
|
(0x04, 0x0000), # config4: Not masking any lane errors or FIFO flags.
|
|
(0x05, 0x0000), # config5: Not masking any SYSREF errors, PAPs, or PLL locks.
|
|
(0x06, 0x0000), # config6: Not masking any lane short test or loss of signal.
|
|
(0x08, 0x0000), # config8: DAC A offset correction set to zero (default).
|
|
(0x09, 0x0000), # config9: DAC B offset correction set to zero (default).
|
|
(0x0A, 0x0000), # config10: DAC C offset correction set to zero (default).
|
|
(0x0B, 0x0000), # config11: DAC D offset correction set to zero (default).
|
|
(0x0C, 0x0400), # config12: Default quadrature correction gain A (AB path).
|
|
(0x0D, 0x0400), # config13: Default coarse mixing options; default quadrature correction gain B (AB path).
|
|
(0x0E, 0x0400), # config14: Default quadrature correction gain A (CD path).
|
|
(0x0F, 0x0400), # config15: No output delays to the DACs; default quadrature correction gain A (CD path).
|
|
(0x10, 0x0000), # config16: Default QMC correction phase (AB path).
|
|
(0x11, 0x0000), # config17: Default QMC correction phase (AD path).
|
|
(0x12, 0x0000), # config18: Phase offset for NCO in DACAB path (default).
|
|
(0x13, 0x0000), # config19: Phase offset for NCO in DACAB path (default).
|
|
(0x14, 0x0000), # config20: Lower 16 bits of NCO Frequency adjust word for DACAB path (default).
|
|
(0x15, 0x0000), # config21: Middle 16 bits of NCO Frequency adjust word for DACAB path (default).
|
|
(0x16, 0x0000), # config22: Upper 16 bits of NCO Frequency adjust word for DACAB path (default).
|
|
(0x17, 0x0000), # config23: Lower 16 bits of NCO Frequency adjust word for DACCD path (default).
|
|
(0x18, 0x0000), # config24: Middle 16 bits of NCO Frequency adjust word for DACCD path (default).
|
|
(0x19, 0x0000), # config25: Upper 16 bits of NCO Frequency adjust word for DACCD path (default).
|
|
(0x1A, 0x0023), # config26: DAC PLL in sleep mode; DAC C & D in sleep mode.
|
|
(0x1B, 0x0000), # config27: Testing settings (default).
|
|
(0x1E, 0x2222), # config30: Sync source for the QMC offset and correction: SYSREF only (Based on VST2).
|
|
(0x1F, 0x2220), # config31: Sync source for mixers and NCO accums.: SYSREF only (Based on VST2).
|
|
(0x20, 0x0000), # config32: Sync source for the dithering, PA protection, and FIR filter blocks: none (Based on VST2).
|
|
(0x22, 0x1B1B), # config34: JESD and DAC routing paths (default).
|
|
(0x23, 0x01FF), # config35: SLEEP signal from pin allowed to reach all blocks, the pin is not even used.
|
|
(0x24, 0x0000), # config36: SYSREF syncs clock dividers: use no pulses yet.
|
|
(0x25, 0x2000), # config37: DACCLK divider to generate JESD clock: div2. (TI recommendation).
|
|
(0x26, 0x0000), # config38: Dithering disabled default).
|
|
(0x2D, 0x0000), # config45: Power amplifier protection settings (default).
|
|
(0x2E, 0xFFFF), # config46: Power amplifier protection threshold (default).
|
|
(0x2F, 0x0004), # config47: Default values.
|
|
(0x30, 0x0000), # config48: Constant value sent to DAC when sifdac_ena is asserted (default).
|
|
(0x31, 0x1000), # config49: DAC PLL in reset and disabled. DACCLK is 491.52 MHz since we bypass the PLL.
|
|
(0x32, 0x0000), # config50: DAC PLL's VCO feedback and prescaler divided (not used).
|
|
(0x33, 0x0000), # config51: DAC PLL VCO and CP settings (not used).
|
|
(0x34, 0x0000), # config52: SYNCB electrical configuration (default @ 1.2V CMV).
|
|
(0x3B, 0x0000), # config59: SerDes PLL's reference. Source: DACCLK / Divider: 1 (0 +1).
|
|
(0x3C, 0x1828), # config60: SerDes PLL Control: high loop BW; high range VCO; multiply factor x5.
|
|
(0x3D, 0x0088), # config61: Upper configuration info for SerDes receivers (TI recommendation).
|
|
(0x3E, 0x0128), # config62: Upper configuration for SerDes receivers:AC coupling; half rate; 20-bit width.
|
|
(0x3F, 0x0000), # config63: No SerDes lanes inversion (default).
|
|
(0x46, self.lanes_ids_1), # config70: JESD ID for lanes 0, 1, and 2.
|
|
(0x47, self.lanes_ids_2), # config71: JESD ID for lanes 3, 4, and 5.
|
|
(0x48, 0x31C3), # config72: JESD ID for lanes 6, and 7; JESD204B supported version and class (default).
|
|
(0x49, 0x0000), # config73: JESD lanes assignment to links (default 0).
|
|
(0x4A, 0x0F3E), # config74: Lanes 0-3 enabled; test seq. disabled; disable clocks to C/D paths.
|
|
(0x4B, 0x1700), # config75: RBD = 24 (23 + 1) (Release Buffer Delay); F = 1 (octets per frame).
|
|
(0x4C, 0x1703), # config76: K = 24 (23 + 1) (frames in multiframe); L = 4 (3 + 1) (number of lanes).
|
|
(0x4D, 0x0100), # config77: M = 2 (1+1) (number of converters); S = 1 (0+1) (number of samples per frame).
|
|
(0x4E, 0x0F4F), # config78: HD = 1 (High Density mode enabled, samples split across lanes).
|
|
(0x4F, 0x1CC1), # config79: Match /R/ char; ILA is supported at TX.
|
|
(0x50, 0x0000), # config80: Lane config data (link0), not used by 37J82 (default).
|
|
(0x51, 0x00FF), # config81: Erros that cause a SYNC request (link0): all selected (default).
|
|
(0x52, 0x00FF), # config82: Errors that are counted in err_c (link0): all selected (default).
|
|
(0x53, 0x0000), # config83: Lane config data (link1), not used by 37J82 (default).
|
|
(0x54, 0x0000), # config84: Erros that cause a SYNC request (link1): none selected.
|
|
(0x55, 0x0000), # config85: Errors that are counted in err_c (link1): none selected.
|
|
(0x56, 0x0000), # config86: Lane config data (link2), not used by 37J82 (default).
|
|
(0x57, 0x0000), # config87: Erros that cause a SYNC request (link2): none selected.
|
|
(0x58, 0x0000), # config88: Errors that are counted in err_c (link2): none selected.
|
|
(0x59, 0x0000), # config89: Lane config data (link3), not used by 37J82 (default).
|
|
(0x5A, 0x0000), # config90: Erros that cause a SYNC request (link2): none selected.
|
|
(0x5B, 0x0000), # config91: Errors that are counted in err_c (link3): none selected.
|
|
(0x5C, 0x0000), # config92: Links 3:1 don't use SYSREF pulses; link 0 uses no pulses yet.
|
|
(0x5E, 0x0000), # config94: Cheksum bits for ILA, not used in 37J82 (default).
|
|
(0x5F, self.octetpath_sel), # config95: Mapping SerDes lanes (0-3) to JESD lanes.
|
|
(0x60, 0x4567), # config96: Mapping SerDes lanes (4-7) to JESD lanes (default).
|
|
(0x61, 0x0001), # config97: Use only link 0 to trigger the SYNCB LVDS output.
|
|
(0x64, 0x0703), # config100: Write to lane 0 errors to clear them (based on VST2).
|
|
(0x65, 0x0703), # config101: Write to lane 1 errors to clear them (based on VST2).
|
|
(0x66, 0x0703), # config102: Write to lane 2 errors to clear them (based on VST2).
|
|
(0x67, 0x0703), # config103: Write to lane 3 errors to clear them (based on VST2).
|
|
(0x68, 0x0703), # config104: Write to lane 4 errors to clear them (based on VST2).
|
|
(0x69, 0x0703), # config105: Write to lane 5 errors to clear them (based on VST2).
|
|
(0x6A, 0x0703), # config106: Write to lane 6 errors to clear them (based on VST2).
|
|
(0x6B, 0x0703), # config107: Write to lane 7 errors to clear them (based on VST2).
|
|
(0x6C, 0x0000), # config108: Rewrite the PLLs alarm bits clearing register.
|
|
(0x6D, 0x0000), # config109: JESD short test alarms (default).
|
|
(0x6E, 0x0000), # config110: Delay fractional filter settings (default).
|
|
(0x6F, 0x0000), # config111: Delay fractional filter settings (default).
|
|
(0x70, 0x0000), # config112: Delay fractional filter settings (default).
|
|
(0x71, 0x0000), # config113: Delay fractional filter settings (default).
|
|
(0x72, 0x0000), # config114: Delay fractional filter settings (default).
|
|
(0x73, 0x0000), # config115: Delay fractional filter settings (default).
|
|
(0x74, 0x0000), # config116: Delay fractional filter settings (default).
|
|
(0x75, 0x0000), # config117: Delay fractional filter settings (default).
|
|
(0x76, 0x0000), # config118: Delay fractional filter settings (default).
|
|
(0x77, 0x0000), # config119: Delay fractional filter settings (default).
|
|
(0x78, 0x0000), # config120: Delay fractional filter settings (default).
|
|
(0x79, 0x0000), # config121: Delay fractional filter settings (default).
|
|
(0x7A, 0x0000), # config122: Delay fractional filter settings (default).
|
|
(0x7B, 0x0000), # config123: Delay fractional filter settings (default).
|
|
(0x7C, 0x0000), # config124: Delay fractional filter settings (default).
|
|
(0x7D, 0x0000), # config125: Delay fractional filter settings (default).
|
|
(0x02, 0x2002), # Deassert the reset for the SIF registers
|
|
))
|
|
self.log.trace("DAC register dump finished.")
|
|
|
|
self.log.trace("Polling for PLL lock...")
|
|
locked = False
|
|
for _ in range(6):
|
|
time.sleep(0.001)
|
|
# Clear stickies possibly?
|
|
self.regs.poke16(0x6C, 0x0000) # Clear alarm bits for PLLs
|
|
if _check_pll_lock():
|
|
locked = True
|
|
self.log.info("DAC PLL Locked!")
|
|
break
|
|
if not locked:
|
|
raise RuntimeError("DAC PLL did not lock! Check the logs for details.")
|
|
|
|
|
|
def enable_sysref_capture(self, enabled=False):
|
|
"""
|
|
Enable the SYSREF capture block, and enable divider's reset.
|
|
"""
|
|
self.log.trace("%s DAC SYSREF capture...",
|
|
{True: 'Enabling', False: 'Disabling'}[enabled])
|
|
cdrvser_sysref_mode = 0b001 if enabled else 0b000
|
|
sysref_mode_link0 = 0b001 if enabled else 0b000
|
|
self.regs.poke16(0x24, cdrvser_sysref_mode << 4) # Enable next SYSREF to reset the clock dividers.
|
|
self.regs.poke16(0x5C, sysref_mode_link0 << 0) # Enable next SYSREF pulse capture for link 0.
|
|
self.log.trace("DAC SYSREF capture %s." % {True: 'enabled', False: 'disabled'}[enabled])
|
|
|
|
|
|
def init_deframer(self):
|
|
"""
|
|
Initialize the DAC's framer.
|
|
"""
|
|
self.log.trace("Initializing framer...")
|
|
self.pokes16((
|
|
(0x4A, 0x0F3F), # config74: Deassert JESD204B block reset.
|
|
(0x4A, 0x0F21), # config74: Set JESD204B to exit init state.
|
|
))
|
|
self.log.trace("DAC deframer initialized.")
|
|
|
|
|
|
def check_deframer_status(self):
|
|
"""
|
|
This function checks the status of the framer by checking alarms.
|
|
"""
|
|
ALARM_ERRORS_DESCRIPTION = {
|
|
15 : "Multiframe alignment error",
|
|
14 : "Frame alignment error",
|
|
13 : "Link configuration error",
|
|
12 : "Elastic buffer overflow",
|
|
11 : "Elastic buffer match error",
|
|
10 : "Code synchronization error",
|
|
9 : "8b/10b not-in-table code error",
|
|
8 : "8b/10b disparity error",
|
|
3 : "FIFO write_error",
|
|
2 : "FIFO write_full",
|
|
1 : "FIFO read_error",
|
|
0 : "FIFO read_empty"
|
|
}
|
|
|
|
self.log.trace("Checking DAC's deframer status.")
|
|
# Clear lane alarms.
|
|
for addr in (0x64, 0x65, 0x66, 0x67):
|
|
self.regs.poke16(addr, 0x0000)
|
|
time.sleep(0.001)
|
|
|
|
# Read lane's alarms
|
|
lanes_alarms_ary = []
|
|
lane = 0
|
|
for addr in (0x64, 0x65, 0x66, 0x67):
|
|
lanes_alarms_ary.insert(lane, self.regs.peek16(addr) & 0xFF0F)
|
|
self.log.trace("Lane {} alarms rb: 0x{:X}".format(lane, lanes_alarms_ary[lane]))
|
|
lane += 1
|
|
|
|
enable_analog_output = True
|
|
# Report warnings based on an error matrix (register_width * lanes).
|
|
errors_ary = []
|
|
for error in range(0, 16):
|
|
errors_ary.insert(error, [])
|
|
# Extract errors from lanes.
|
|
for lane in range(0, len(lanes_alarms_ary)):
|
|
if lanes_alarms_ary[lane] & (0b1 << error) > 0:
|
|
errors_ary[error].append(lane)
|
|
if len(errors_ary[error]) > 0:
|
|
enable_analog_output = False
|
|
self.log.warning(ALARM_ERRORS_DESCRIPTION[error] +
|
|
" in lane(s): " + ' '.join(map(str, errors_ary[error])))
|
|
|
|
self.tx_enable(enable_analog_output)
|
|
self.log.debug("%s analog TX output.",
|
|
{True: 'Enabling', False: 'Disabling'}[enable_analog_output])
|
|
return enable_analog_output
|
|
|
|
|
|
def test_mode(self, mode='PRBS-31', lane=0):
|
|
"""
|
|
This method enables the DAC's test mode to verify the SerDes.
|
|
Users should monitor the ALARM pin to see the results of the test.
|
|
If the test is failing, ALARM will be high (or toggling if marginal).
|
|
If the test is passing, the ALARM will be low.
|
|
"""
|
|
MODE_VAL = {'OFF': 0x0, 'PRBS-7': 0x2, 'PRBS-23': 0x3, 'PRBS-31': 0x4}
|
|
assert mode.upper() in MODE_VAL
|
|
assert lane in range(0, 8)
|
|
self.log.debug("Setting test mode for lane {} at the DAC: {}.".format(lane, mode))
|
|
# To run the PRBS test on the DAC, users first need to setup the DAC for
|
|
# normal use, then make the following SPI writes:
|
|
# 1. config74, set bits 4:0 to 0x1E to disable JESD clock.
|
|
addr = 0x4A
|
|
rb = self.regs.peek16(addr)
|
|
data_w = (rb & ~0x001F) | 0x001E if mode != 'OFF' else 0x0F3E
|
|
self.log.trace("Writing register {:02X} with {:04X}".format(addr, data_w))
|
|
self.regs.poke16(addr, data_w)
|
|
# 2. config61, set bits 14:12 to 0x2 to enable the 7-bit PRBS test pattern; or
|
|
# set bits 14:12 to 0x3 to enable the 23-bit PRBS test pattern; or
|
|
# set bits 14:12 to 0x4 to enable the 31-bit PRBS test pattern.
|
|
addr = 0x3D
|
|
rb = self.regs.peek16(addr)
|
|
data_w = (rb & ~0x7000) | (MODE_VAL[mode] << 12)
|
|
self.log.trace("Writing register {:02X} with {:04X}".format(addr, data_w))
|
|
self.regs.poke16(addr, data_w)
|
|
# 3. config27, set bits 11:8 to 0x3 to output PRBS testfail on ALARM pin.
|
|
# 4. config27, set bits 14:12 to the lane to be tested (0 through 7).
|
|
addr = 0x1B
|
|
rb = self.regs.peek16(addr)
|
|
data_w = (rb & ~0x7F00) | (0x3 << 8) | (lane << 12) if mode != 'OFF' else 0x0000
|
|
self.log.trace("Writing register {:02X} with {:04X}".format(addr, data_w))
|
|
self.regs.poke16(addr, data_w)
|
|
# 5. config62, make sure bits 12:11 are set to 0x0 to disable character alignment.
|
|
addr = 0x3E
|
|
rb = self.regs.peek16(addr)
|
|
if ((rb & 0x58) >> 11) != 0x0:
|
|
self.log.error("Char alignment is enabled when not expected.")
|