aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/periph_manager/n3xx.py
diff options
context:
space:
mode:
Diffstat (limited to 'mpm/python/usrp_mpm/periph_manager/n3xx.py')
-rw-r--r--mpm/python/usrp_mpm/periph_manager/n3xx.py82
1 files changed, 74 insertions, 8 deletions
diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx.py b/mpm/python/usrp_mpm/periph_manager/n3xx.py
index 373689521..ec7fedad9 100644
--- a/mpm/python/usrp_mpm/periph_manager/n3xx.py
+++ b/mpm/python/usrp_mpm/periph_manager/n3xx.py
@@ -13,14 +13,16 @@ import copy
import shutil
import subprocess
import json
+import time
import datetime
import threading
from six import iteritems, itervalues
from builtins import object
+from usrp_mpm.cores import WhiteRabbitRegsControl
from usrp_mpm.gpsd_iface import GPSDIface
from usrp_mpm.periph_manager import PeriphManagerBase
from usrp_mpm.mpmtypes import SID
-from usrp_mpm.mpmutils import assert_compat_number, str2bool
+from usrp_mpm.mpmutils import assert_compat_number, str2bool, poll_with_timeout
from usrp_mpm.rpc_server import no_rpc
from usrp_mpm.sys_utils import dtoverlay
from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO, GPIOBank
@@ -35,7 +37,7 @@ N3XX_DEFAULT_ENABLE_GPS = True
N3XX_DEFAULT_ENABLE_FPGPIO = True
N3XX_FPGA_COMPAT = (5, 2)
N3XX_MONITOR_THREAD_INTERVAL = 1.0 # seconds
-N3XX_SFP_TYPES = {0:"", 1:"1G", 2:"10G", 3:"A"}
+N3XX_SFP_TYPES = {0:"", 1:"1G", 2:"10G", 3:"A", 4:"W"}
###############################################################################
# Additional peripheral controllers specific to Magnesium
@@ -193,6 +195,8 @@ class MboardRegsControl(object):
MB_CLOCK_CTRL_PPS_SEL_INT_25 = 1
MB_CLOCK_CTRL_PPS_SEL_EXT = 2
MB_CLOCK_CTRL_PPS_SEL_GPSDO = 3
+ MB_CLOCK_CTRL_PPS_SEL_SFP0 = 5
+ MB_CLOCK_CTRL_PPS_SEL_SFP1 = 6
MB_CLOCK_CTRL_PPS_OUT_EN = 4 # output enabled = 1
MB_CLOCK_CTRL_MEAS_CLK_RESET = 12 # set to 1 to reset mmcm, default is 0
MB_CLOCK_CTRL_MEAS_CLK_LOCKED = 13 # locked indication for meas_clk mmcm
@@ -317,11 +321,20 @@ class MboardRegsControl(object):
elif time_source == 'gpsdo':
self.log.debug("Setting time source to gpsdo...")
pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_GPSDO
+ elif time_source == 'sfp0':
+ self.log.debug("Setting time source to sfp0...")
+ pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_SFP0
+ elif time_source == 'sfp1':
+ self.log.debug("Setting time source to sfp1...")
+ pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_SFP1
else:
assert False
+
with self.regs.open():
- reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFFF0
- reg_val = reg_val | (pps_sel_val & 0xF)
+ reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFF90
+ # prevent glitches by writing a cleared value first, then the final value.
+ self.poke32(self.MB_CLOCK_CTRL, reg_val)
+ reg_val = reg_val | (pps_sel_val & 0x6F)
self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
self.poke32(self.MB_CLOCK_CTRL, reg_val)
@@ -397,12 +410,26 @@ class MboardRegsControl(object):
return "XA"
elif (sfp0_type == "A") and (sfp1_type == "A"):
return "AA"
+ elif (sfp0_type == "W") and (sfp1_type == "10G"):
+ return "WX"
else:
self.log.warning("Unrecognized SFP type combination: ({}, {})".format(
sfp0_type, sfp1_type
))
return ""
+ def get_sfp0_link_status(self):
+ """
+ Reads the type of the FPGA image currently loaded
+ Returns a string with the type (ie HG, XG, AA, etc.)
+ """
+ with self.regs.open():
+ sfp0_info_rb = self.peek32(self.MB_SFP0_INFO)
+ # Print the registers values as 32-bit hex values
+ self.log.trace("SFP0 Info: 0x{0:0{1}X}".format(sfp0_info_rb, 8))
+ sfp0_link = bool((sfp0_info_rb & 0x00010000) >> 16)
+ return sfp0_link
+
###############################################################################
# Transport managers
@@ -483,6 +510,8 @@ class n3xx(PeriphManagerBase):
# N3xx-specific settings
# Label for the mboard UIO
mboard_regs_label = "mboard-regs"
+ # Label for the white rabbit UIO
+ wr_regs_label = "wr-regs"
# Override the list of updateable components
updateable_components = {
'fpga': {
@@ -627,6 +656,7 @@ class n3xx(PeriphManagerBase):
self.log.trace("Enabling power of MGT156MHZ clk")
self._gpios.set("PWREN-CLK-MGT156MHz")
self.enable_1g_ref_clock()
+ self.enable_wr_ref_clock()
self.enable_gps(
enable=str2bool(
args.get('enable_gps', N3XX_DEFAULT_ENABLE_GPS)
@@ -892,7 +922,7 @@ class n3xx(PeriphManagerBase):
def get_time_sources(self):
" Returns list of valid time sources "
- return ['internal', 'external', 'gpsdo']
+ return ['internal', 'external', 'gpsdo', 'sfp0']
def get_time_source(self):
" Return the currently selected time source "
@@ -902,8 +932,36 @@ class n3xx(PeriphManagerBase):
" Set a time source "
assert time_source in self.get_time_sources()
self._time_source = time_source
- self.mboard_regs_control.set_time_source(
- time_source, self.get_ref_clock_freq())
+ self.mboard_regs_control.set_time_source(time_source, self.get_ref_clock_freq())
+ if time_source == 'sfp0':
+ # This error is specific to slave and master mode for White Rabbit.
+ # Grand Master mode will require the external or gpsdo sources (not supported).
+ if (time_source == 'sfp0' or time_source == 'sfp1') and \
+ self.get_clock_source() != 'internal':
+ error_msg = "Time source sfp(0|1) requires the internal clock source!"
+ self.log.error(error_msg)
+ raise RuntimeError(error_msg)
+ if self.updateable_components['fpga']['type'] != 'WX':
+ self.log.error("{} time source requires 'WX' FPGA type" \
+ .format(time_source))
+ raise RuntimeError("{} time source requires 'WX' FPGA type" \
+ .format(time_source))
+ # Only open UIO to the WR core once we're guaranteed it exists.
+ wr_regs_control = WhiteRabbitRegsControl(
+ self.wr_regs_label, self.log)
+ # Wait for time source to become ready. Only applies to SFP0/1. All other
+ # targets start their PPS immediately.
+ self.log.debug("Waiting for {} timebase to lock..." \
+ .format(time_source))
+ if not poll_with_timeout(
+ lambda: wr_regs_control.get_time_lock_status(),
+ 40000, # Try for x ms... this number is set from a few benchtop tests
+ 1000, # Poll every... second! why not?
+ ):
+ self.log.error("{} timebase failed to lock within 40 seconds. Status: 0x{:X}" \
+ .format(time_source, wr_regs_control.get_time_lock_status()))
+ raise RuntimeError("Failed to lock SFP timebase.")
+
def set_fp_gpio_master(self, value):
"""set driver for front panel GPIO
@@ -980,7 +1038,7 @@ class n3xx(PeriphManagerBase):
Enables 125 MHz refclock for 1G interface.
"""
self.log.trace("Enable 125 MHz Clock for 1G SFP interface.")
- self._gpios.set("NETCLK-CE")
+ self._gpios.set("NETCLK-CE", 1)
self._gpios.set("NETCLK-RESETn", 0)
self._gpios.set("NETCLK-PR0", 1)
self._gpios.set("NETCLK-PR1", 1)
@@ -991,6 +1049,14 @@ class n3xx(PeriphManagerBase):
self.log.trace("Finished configuring NETCLK CDCM.")
self._gpios.set("NETCLK-RESETn", 1)
+ def enable_wr_ref_clock(self):
+ """
+ Enables 20 MHz WR refclk. Note that enable_1g_ref_clock() is also required for this
+ interface to work, although calling it here is redundant.
+ """
+ self.log.trace("Enable White Rabbit reference clock.")
+ self._gpios.set("PWREN-CLK-WB-20MHz", 1)
+
###########################################################################
# Sensors
###########################################################################