aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/dboard_manager/magnesium.py
diff options
context:
space:
mode:
authorDJ Epson <daniel.jepson@ni.com>2017-09-01 15:20:09 -0700
committerMartin Braun <martin.braun@ettus.com>2017-12-22 15:04:01 -0800
commitf141f71180e397fa21d23385f154da138bef0a81 (patch)
treedd68c27564157cbe8ecaac0056008ec286ddd704 /mpm/python/usrp_mpm/dboard_manager/magnesium.py
parent04b0f414b9235d36e7572b8c6644630d77de4aab (diff)
downloaduhd-f141f71180e397fa21d23385f154da138bef0a81.tar.gz
uhd-f141f71180e397fa21d23385f154da138bef0a81.tar.bz2
uhd-f141f71180e397fa21d23385f154da138bef0a81.zip
mpm: Streaming-related improvements to Magnesium controls
Diffstat (limited to 'mpm/python/usrp_mpm/dboard_manager/magnesium.py')
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/magnesium.py177
1 files changed, 157 insertions, 20 deletions
diff --git a/mpm/python/usrp_mpm/dboard_manager/magnesium.py b/mpm/python/usrp_mpm/dboard_manager/magnesium.py
index 536503b62..55a53b984 100644
--- a/mpm/python/usrp_mpm/dboard_manager/magnesium.py
+++ b/mpm/python/usrp_mpm/dboard_manager/magnesium.py
@@ -49,6 +49,26 @@ def create_spidev_iface(dev_node):
SPI_WRIT_FLAG
)
+def create_spidev_iface_cpld(dev_node):
+ """
+ Create a regs iface from a spidev node
+ """
+ SPI_SPEED_HZ = 1000000
+ SPI_MODE = 0
+ SPI_ADDR_SHIFT = 16
+ SPI_DATA_SHIFT = 0
+ SPI_READ_FLAG = 1<<23
+ SPI_WRIT_FLAG = 0
+ return lib.spi.make_spidev_regs_iface(
+ dev_node,
+ SPI_SPEED_HZ,
+ SPI_MODE,
+ SPI_ADDR_SHIFT,
+ SPI_DATA_SHIFT,
+ SPI_READ_FLAG,
+ SPI_WRIT_FLAG
+ )
+
class Magnesium(DboardManagerBase):
"""
Holds all dboard specific information and methods of the magnesium dboard
@@ -61,36 +81,92 @@ class Magnesium(DboardManagerBase):
pids = [0x150]
# Maps the chipselects to the corresponding devices:
- spi_chipselect = {"lmk": 0, "mykonos": 1}
+ spi_chipselect = {"cpld": 0, "lmk": 1, "mykonos": 2}
+
+ def _get_mykonos_function(self, name):
+ mykfunc = getattr(self.mykonos, name)
+ def func(*args):
+ return mykfunc(*args)
+ func.__doc__ = mykfunc.__doc__
+ return func
def __init__(self, slot_idx, **kwargs):
super(Magnesium, self).__init__(slot_idx, **kwargs)
self.log = get_logger("Magnesium-{}".format(slot_idx))
self.log.trace("Initializing Magnesium daughterboard, slot index {}".format(self.slot_idx))
- def init(self, args):
- """
- Execute necessary init dance to bring up dboard
- """
- self.log.info("init() called with args `{}'".format(
- ",".join(['{}={}'.format(x, args[x]) for x in args])
- ))
- self.clock_regs = create_spidev_iface(self._spi_nodes['lmk'])
+ self.log.debug("Loading C++ drivers for CPLD SPI.")
+ self.cpld_regs = create_spidev_iface_cpld(self._spi_nodes['cpld'])
+
self.log.debug("Loading C++ drivers...")
self._device = lib.dboards.magnesium_manager(
self._spi_nodes['mykonos'],
)
self.spi_lock = self._device.get_spi_lock()
+ self.log.debug("Loading C++ drivers for LMK SPI.")
+ self.clock_regs = create_spidev_iface(self._spi_nodes['lmk'])
+ self.lmk = LMK04828Mg(self.clock_regs, self.spi_lock)
self.mykonos = self._device.get_radio_ctrl()
self.log.debug("Loaded C++ drivers.")
- self.lmk = LMK04828Mg(self.clock_regs, self.spi_lock)
self.log.debug("Getting Mg A uio...")
- self.radio_regs = UIO(label="jesd204b-regs", read_only=False)
+ self.radio_regs = UIO(label="dboard-regs-0", read_only=False)
self.log.info("Radio-register UIO object successfully generated!")
+
+ for mykfuncname in [x for x in dir(self.mykonos) if not x.startswith("_") and callable(getattr(self.mykonos, x))]:
+ self.log.trace("adding {}".format(mykfuncname))
+ setattr(self, mykfuncname, self._get_mykonos_function(mykfuncname))
+
+ def init(self, args):
+ """
+ Execute necessary init dance to bring up dboard
+ """
+
+ def _init_clock_control(dboard_regs):
+ " Create a dboard clock control object and reset it "
+ dboard_clk_control = DboardClockControl(dboard_regs, self.log)
+ dboard_clk_control.reset_mmcm()
+ return dboard_clk_control
+
+
+ self.log.info("init() called with args `{}'".format(
+ ",".join(['{}={}'.format(x, args[x]) for x in args])
+ ))
+
+
+ self.dboard_clk_control = _init_clock_control(self.radio_regs)
+
+ self.lmk.init()
+ self.lmk.config()
+ self.dboard_clk_control.enable_mmcm()
+ self.log.info("Clocking Configured Successfully!")
+
self.init_jesd(self.radio_regs)
+
+ self.mykonos.start_radio()
+
return True
+
+
+ def cpld_peek16(self, addr):
+ return self.cpld_regs.peek16(addr)
+
+ def cpld_poke16(self, addr, data):
+ return self.cpld_regs.poke16(addr, data)
+
+ def lmk_peek(self, addr):
+ return self.lmk.peek8(addr)
+
+ def lmk_poke(self, addr, data):
+ return self.lmk.poke8(addr, data)
+
+ def lmk_setup_dbg(self):
+ self.lmk.init()
+ self.lmk.config()
+ return "LMK Init Success"
+
+
def init_jesd(self, uio):
"""
Bring up the JESD link between Mykonos and the N310
@@ -101,16 +177,17 @@ class Magnesium(DboardManagerBase):
self.log.trace("Checking JESD core...")
self.jesdcore.check_core()
self.log.trace("Initializing LMK...")
- self.lmk.init()
- self.radio_regs.poke32(0x2078, 0xA000040)
- self.log.trace("Verify LMK Chip ID...")
- self.lmk.verify_chip_id()
- self.jesdcore.unreset_mmcm()
+ self.jesdcore.unreset_qpll()
self.jesdcore.init()
self.log.trace("Resetting Mykonos...")
- self.jesdcore.reset_mykonos() #not sure who owns the reset
+
+ # YIKES!!! Where does this go?!? CPLD?
+ # self.jesdcore.reset_mykonos() #not sure who owns the reset
+ self.cpld_poke16(0x13, 0x1)
+ self.cpld_poke16(0x13, 0x0)
+
self.log.trace("Initializing Mykonos...")
self.mykonos.begin_initialization()
@@ -134,12 +211,15 @@ class Magnesium(DboardManagerBase):
time.sleep(0.2)
if not self.jesdcore.get_framer_status():
raise Exception('JESD Core Framer is not synced!')
+ if ((self.mykonos.get_deframer_status() & 0x7F) != 0x28):
+ raise Exception('Mykonos Deframer is not synced!')
if not self.jesdcore.get_deframer_status():
raise Exception('JESD Core Deframer is not synced!')
- if (self.mykonos.get_framer_status() & 0xFF != 0x3E):
+ if (self.mykonos.get_framer_status() & 0xFF) != 0x3E:
raise Exception('Mykonos Framer is not synced!')
- if (self.mykonos.get_deframer_status() & 0x7F != 0x28):
- raise Exception('Mykonos Deframer is not synced!')
+ if (self.mykonos.get_multichip_sync_status() & 0xB) != 0xB:
+ raise Exception('Mykonos multi chip sync failed!')
+
self.log.trace("JESD fully synced and ready")
def dump_jesd_core(self):
@@ -149,3 +229,60 @@ class Magnesium(DboardManagerBase):
print(("%08X" % self.radio_regs.peek32(i + j)), end=' ')
print("")
+
+
+class DboardClockControl(object):
+ """
+ Control the FPGA MMCM for Radio Clock control.
+ """
+ # Clocking Register address constants
+ RADIO_CLK_MMCM = 0x0020
+ PHASE_SHIFT_CONTROL = 0x0024
+ RADIO_CLK_ENABLES = 0x0028
+ MGT_REF_CLK_STATUS = 0x0030
+
+ def __init__(self, regs, log):
+ self.log = log
+ self.regs = regs
+ self.poke32 = self.regs.poke32
+ self.peek32 = self.regs.peek32
+
+ def enable_outputs(self, enable=True):
+ """
+ Enables or disables the MMCM outputs.
+ """
+ if enable:
+ self.poke32(self.RADIO_CLK_ENABLES, 0x011)
+ else:
+ self.poke32(self.RADIO_CLK_ENABLES, 0x000)
+
+ def reset_mmcm(self):
+ """
+ Uninitialize and reset the MMCM
+ """
+ self.log.trace("Disabling all Radio Clocks, then resetting MMCM...")
+ self.enable_outputs(False)
+ self.poke32(self.RADIO_CLK_MMCM, 0x1)
+
+ def enable_mmcm(self):
+ """
+ Unreset MMCM and poll lock indicators
+
+ If MMCM is not locked after unreset, an exception is thrown.
+ """
+ self.log.trace("Un-resetting MMCM...")
+ self.poke32(self.RADIO_CLK_MMCM, 0x2)
+ time.sleep(0.5) # Replace with poll and timeout TODO
+ mmcm_locked = bool(self.peek32(self.RADIO_CLK_MMCM) & 0x10)
+ if not mmcm_locked:
+ self.log.error("MMCM not locked!")
+ raise RuntimeError("MMCM not locked!")
+ self.log.trace("Enabling output MMCM clocks...")
+ self.enable_outputs(True)
+
+ def check_refclk(self):
+ """
+ Not technically a clocking reg, but related.
+ """
+ return bool(self.peek32(self.MGT_REF_CLK_STATUS) & 0x1)
+