1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
|
#
# 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.")
|