diff options
author | DJ Epson <daniel.jepson@ni.com> | 2017-09-01 15:20:09 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:04:01 -0800 |
commit | f141f71180e397fa21d23385f154da138bef0a81 (patch) | |
tree | dd68c27564157cbe8ecaac0056008ec286ddd704 /mpm/python/usrp_mpm/dboard_manager/magnesium.py | |
parent | 04b0f414b9235d36e7572b8c6644630d77de4aab (diff) | |
download | uhd-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.py | 177 |
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) + |