aboutsummaryrefslogtreecommitdiffstats
path: root/mpm
diff options
context:
space:
mode:
authorBrent Stapleton <brent.stapleton@ettus.com>2017-11-29 17:05:41 -0800
committerMartin Braun <martin.braun@ettus.com>2017-12-22 15:05:58 -0800
commit0bf22a3e57dab4af3bfc66dbae999d3d9d11341b (patch)
treefd8af6de9c1578124f8cbd5d8c21052786d24913 /mpm
parent3cff6298cea5f3699aa5db5921cd6933f557a034 (diff)
downloaduhd-0bf22a3e57dab4af3bfc66dbae999d3d9d11341b.tar.gz
uhd-0bf22a3e57dab4af3bfc66dbae999d3d9d11341b.tar.bz2
uhd-0bf22a3e57dab4af3bfc66dbae999d3d9d11341b.zip
mpm: UIOs now open only when necessary
Refactoring to use the C++-based UIO objects. The Liberio and Ethernet objects now open the UIO before using it, and close it once done. Reviewed-By: Martin Braun <martin.braun@ettus.com>
Diffstat (limited to 'mpm')
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/eiscat.py143
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/magnesium.py44
-rw-r--r--mpm/python/usrp_mpm/ethtable.py39
-rw-r--r--mpm/python/usrp_mpm/liberiotable.py10
-rw-r--r--mpm/python/usrp_mpm/periph_manager/base.py4
-rw-r--r--mpm/python/usrp_mpm/periph_manager/n310.py58
-rw-r--r--mpm/python/usrp_mpm/rpc_server.py9
-rw-r--r--mpm/python/usrp_mpm/sys_utils/uio.py68
8 files changed, 190 insertions, 185 deletions
diff --git a/mpm/python/usrp_mpm/dboard_manager/eiscat.py b/mpm/python/usrp_mpm/dboard_manager/eiscat.py
index bfd3b42a9..89244d661 100644
--- a/mpm/python/usrp_mpm/dboard_manager/eiscat.py
+++ b/mpm/python/usrp_mpm/dboard_manager/eiscat.py
@@ -201,10 +201,11 @@ class DboardClockControl(object):
"""
Enables or disables the MMCM outputs.
"""
- if enable:
- self.poke32(self.RADIO_CLK_ENABLES, 0x011)
- else:
- self.poke32(self.RADIO_CLK_ENABLES, 0x000)
+ with self.regs.open():
+ if enable:
+ self.poke32(self.RADIO_CLK_ENABLES, 0x011)
+ else:
+ self.poke32(self.RADIO_CLK_ENABLES, 0x000)
def reset_mmcm(self):
"""
@@ -212,7 +213,8 @@ class DboardClockControl(object):
"""
self.log.trace("Disabling all Radio Clocks, then resetting MMCM...")
self.enable_outputs(False)
- self.poke32(self.RADIO_CLK_MMCM, 0x1)
+ with self.regs.open():
+ self.poke32(self.RADIO_CLK_MMCM, 0x1)
def enable_mmcm(self):
"""
@@ -221,20 +223,23 @@ class DboardClockControl(object):
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)
+ with self.regs.open():
+ 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)
+ with self.regs.open():
+ return bool(self.peek32(self.MGT_REF_CLK_STATUS) & 0x1)
+
class JesdCoreEiscat(object):
@@ -273,17 +278,18 @@ class JesdCoreEiscat(object):
Verify that the JESD core ID is correct.
"""
expected_id = self.CORE_ID_BASE + self.core_idx
- core_id = self.peek32(self.JESD_SIGNATURE_REG)
- self.log.trace("Reading JESD core ID: {:x}".format(core_id))
- if core_id != expected_id:
- self.log.error(
- "Cannot identify JESD core! Read ID: {:x} Expected: {:x}".format(
- core_id, expected_id
+ with self.regs.open():
+ core_id = self.peek32(self.JESD_SIGNATURE_REG)
+ self.log.trace("Reading JESD core ID: {:x}".format(core_id))
+ if core_id != expected_id:
+ self.log.error(
+ "Cannot identify JESD core! Read ID: {:x} Expected: {:x}".format(
+ core_id, expected_id
+ )
)
- )
- return False
- date_info = core_id = self.peek32(self.JESD_REVISION_REG)
- self.log.trace("Reading JESD date info: {:x}".format(date_info))
+ return False
+ date_info = core_id = self.peek32(self.JESD_REVISION_REG)
+ self.log.trace("Reading JESD date info: {:x}".format(date_info))
return True
def init(self):
@@ -306,11 +312,12 @@ class JesdCoreEiscat(object):
Returns nothing, but throws on error.
"""
self.log.trace("Init JESD Deframer...")
- self.poke32(0x40, 0x02) # Force assertion of ADC SYNC
- self.poke32(0x50, 0x01) # Data = 0 = Scrambler enabled. Data = 1 = disabled. Must match ADC settings.
- if not self._gt_rx_reset(reset_only=False):
- raise RuntimeError("JESD Core did not come out of reset properly!")
- self.poke32(0x40, 0x00) # Stop forcing assertion of ADC SYNC
+ with self.regs.open():
+ self.poke32(0x40, 0x02) # Force assertion of ADC SYNC
+ self.poke32(0x50, 0x01) # Data = 0 = Scrambler enabled. Data = 1 = disabled. Must match ADC settings.
+ if not self._gt_rx_reset(reset_only=False):
+ raise RuntimeError("JESD Core did not come out of reset properly!")
+ self.poke32(0x40, 0x00) # Stop forcing assertion of ADC SYNC
def check_deframer_status(self):
"""
@@ -328,8 +335,9 @@ class JesdCoreEiscat(object):
"""
Power down unused CPLLs and QPLLs
"""
- self.poke32(0x00C, 0xFFFC0000)
- self.log.trace("MGT power enabled readback: {:x}".format(self.peek32(0x00C)))
+ with self.regs.open():
+ self.poke32(0x00C, 0xFFFC0000)
+ self.log.trace("MGT power enabled readback: {:x}".format(self.peek32(0x00C)))
def _gt_rx_reset(self, reset_only=True):
"""
@@ -338,34 +346,36 @@ class JesdCoreEiscat(object):
Returns True on success.
"""
- self.poke32(0x024, 0x10) # Place the RX MGTs in reset
- if not reset_only:
- time.sleep(.001) # Probably not necessary
- self.poke32(0x024, 0x20) # Unreset and Enable
- time.sleep(0.1) # TODO replace with poll and timeout 20 ms
- self.log.trace("MGT power enabled readback (rst seq): {:x}".format(self.peek32(0x00C)))
- self.log.trace("MGT CPLL lock readback (rst seq): {:x}".format(self.peek32(0x004)))
- lock_status = self.peek32(0x024)
- if lock_status & 0xFFFF0000 != 0x30000:
- self.log.error(
- "JESD Core {}: RX MGTs failed to reset! Status: 0x{:x}".format(self.core_idx, lock_status)
- )
- return False
+ with self.regs.open():
+ self.poke32(0x024, 0x10) # Place the RX MGTs in reset
+ if not reset_only:
+ time.sleep(.001) # Probably not necessary
+ self.poke32(0x024, 0x20) # Unreset and Enable
+ time.sleep(0.1) # TODO replace with poll and timeout 20 ms
+ self.log.trace("MGT power enabled readback (rst seq): {:x}".format(self.peek32(0x00C)))
+ self.log.trace("MGT CPLL lock readback (rst seq): {:x}".format(self.peek32(0x004)))
+ lock_status = self.peek32(0x024)
+ if lock_status & 0xFFFF0000 != 0x30000:
+ self.log.error(
+ "JESD Core {}: RX MGTs failed to reset! Status: 0x{:x}".format(self.core_idx, lock_status)
+ )
+ return False
return True
def _gt_pll_lock_control(self):
"""
Make sure PLLs are locked
"""
- self.poke32(0x004, 0x11111111) # Reset CPLLs
- self.poke32(0x004, 0x11111100) # Unreset the ones we're using
- time.sleep(0.02) # TODO replace with poll and timeout
- self.poke32(0x010, 0x10000) # Clear all CPLL sticky bits
- self.log.trace("MGT CPLL lock readback (lock seq): {:x}".format(self.peek32(0x004)))
- lock_status = self.peek32(0x004) & 0xFF
- lock_good = bool(lock_status == 0x22)
- if not lock_good:
- self.log.error("GT PLL failed to lock! Status: 0x{:x}".format(lock_status))
+ with self.regs.open():
+ self.poke32(0x004, 0x11111111) # Reset CPLLs
+ self.poke32(0x004, 0x11111100) # Unreset the ones we're using
+ time.sleep(0.02) # TODO replace with poll and timeout
+ self.poke32(0x010, 0x10000) # Clear all CPLL sticky bits
+ self.log.trace("MGT CPLL lock readback (lock seq): {:x}".format(self.peek32(0x004)))
+ lock_status = self.peek32(0x004) & 0xFF
+ lock_good = bool(lock_status == 0x22)
+ if not lock_good:
+ self.log.error("GT PLL failed to lock! Status: 0x{:x}".format(lock_status))
return lock_good
def _gt_polarity_control(self):
@@ -380,7 +390,8 @@ class JesdCoreEiscat(object):
"JESD Core: Slot {}, ADC {}: Setting polarity control to 0x{:2x}".format(
self.slot, self.core_idx, reg_val
))
- self.poke32(0x80, reg_val)
+ with self.regs.open():
+ self.poke32(0x80, reg_val)
class EISCAT(DboardManagerBase):
@@ -567,16 +578,16 @@ class EISCAT(DboardManagerBase):
# Clocks and PPS are now fully active!
return True
-
def send_sysref(self):
"""
Send a SYSREF from MPM. This is not possible to do in a timed
fashion though.
"""
self.log.trace("Sending SYSREF via MPM...")
- self.radio_regs.poke32(self.SYSREF_CONTROL, 0x0)
- self.radio_regs.poke32(self.SYSREF_CONTROL, 0x1)
- self.radio_regs.poke32(self.SYSREF_CONTROL, 0x0)
+ with self.radio_regs.open():
+ self.radio_regs.poke32(self.SYSREF_CONTROL, 0x0)
+ self.radio_regs.poke32(self.SYSREF_CONTROL, 0x1)
+ self.radio_regs.poke32(self.SYSREF_CONTROL, 0x0)
def init_jesd_core_reset_adcs(self):
"""
@@ -672,10 +683,11 @@ class EISCAT(DboardManagerBase):
# Enable all channels first due to a signal integrity issue when enabling them
# after the LNA enable is asserted.
self.log.trace("Enabling power to the daughterboard...")
- regs.poke32(self.DB_CH_ENABLES, 0x000000FF)
- regs.poke32(self.DB_ENABLES, 0x01000000)
- regs.poke32(self.DB_ENABLES, 0x00010101)
- regs.poke32(self.ADC_CONTROL, 0x00010000)
+ with regs.open():
+ regs.poke32(self.DB_CH_ENABLES, 0x000000FF)
+ regs.poke32(self.DB_ENABLES, 0x01000000)
+ regs.poke32(self.DB_ENABLES, 0x00010101)
+ regs.poke32(self.ADC_CONTROL, 0x00010000)
time.sleep(0.100)
def _deinit_power(self, regs):
@@ -683,9 +695,10 @@ class EISCAT(DboardManagerBase):
Turn off power to the dboard. Sequence is reverse of init_power.
"""
self.log.trace("Disabling power to the daughterboard...")
- regs.poke32(self.ADC_CONTROL, 0x00100000)
- regs.poke32(self.DB_ENABLES, 0x10101010)
- regs.poke32(self.DB_CH_ENABLES, 0x00000000) # Disable all channels (last)
+ with regs.open():
+ regs.poke32(self.ADC_CONTROL, 0x00100000)
+ regs.poke32(self.DB_ENABLES, 0x10101010)
+ regs.poke32(self.DB_CH_ENABLES, 0x00000000) # Disable all channels (last)
def update_ref_clock_freq(self, freq):
"""
diff --git a/mpm/python/usrp_mpm/dboard_manager/magnesium.py b/mpm/python/usrp_mpm/dboard_manager/magnesium.py
index e43f324af..b7471b7c9 100644
--- a/mpm/python/usrp_mpm/dboard_manager/magnesium.py
+++ b/mpm/python/usrp_mpm/dboard_manager/magnesium.py
@@ -29,7 +29,7 @@ from usrp_mpm.dboard_manager import DboardManagerBase
from usrp_mpm.dboard_manager.lmk_mg import LMK04828Mg
from usrp_mpm.cores import nijesdcore
from usrp_mpm.mpmlog import get_logger
-from usrp_mpm.sys_utils.uio import UIO
+from usrp_mpm.sys_utils.uio import UIO, open_uio
from usrp_mpm.sys_utils.udev import get_eeprom_paths
from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO
from usrp_mpm.cores import ClockSynchronizer
@@ -137,14 +137,6 @@ class DboardClockControl(object):
self.poke32 = self.regs.poke32
self.peek32 = self.regs.peek32
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.log.trace("Tearing down DboardClockControl() object...")
- self.regs = None
- return exc_type is None
-
def enable_outputs(self, enable=True):
"""
Enables or disables the MMCM outputs.
@@ -534,15 +526,15 @@ class Magnesium(DboardManagerBase):
# The following peripherals are only used during init, so we don't want
# to hang on to them for the full lifetime of the Magnesium class. This
# helps us close file descriptors associated with the UIO objects.
- dboard_ctrl_regs = UIO(
+ with open_uio(
label="dboard-regs-{}".format(self.slot_idx),
read_only=False
- )
- self.log.trace("Creating jesdcore object...")
- jesdcore = nijesdcore.NIMgJESDCore(dboard_ctrl_regs, self.slot_idx)
- # Now get cracking with the actual init sequence:
- self.log.trace("Creating dboard clock control object...")
- with DboardClockControl(dboard_ctrl_regs, self.log) as db_clk_control:
+ ) as dboard_ctrl_regs:
+ self.log.trace("Creating jesdcore object...")
+ jesdcore = nijesdcore.NIMgJESDCore(dboard_ctrl_regs, self.slot_idx)
+ # Now get cracking with the actual init sequence:
+ self.log.trace("Creating dboard clock control object...")
+ db_clk_control = DboardClockControl(dboard_ctrl_regs, self.log)
self.log.debug("Reset Dboard Clocking and JESD204B interfaces...")
db_clk_control.reset_mmcm()
jesdcore.reset()
@@ -555,18 +547,15 @@ class Magnesium(DboardManagerBase):
self.INIT_PHASE_DAC_WORD,
)
db_clk_control.enable_mmcm()
- self.log.info("Sample Clocks and Phase DAC Configured Successfully!")
- # Synchronize DB Clocks
- _sync_db_clock(_get_clock_synchronizer())
- # Clocks and PPS are now fully active!
- self.mykonos.set_master_clock_rate(self.master_clock_rate)
- self.init_jesd(jesdcore, args)
+ self.log.info("Sample Clocks and Phase DAC Configured Successfully!")
+ # Synchronize DB Clocks
+ _sync_db_clock(_get_clock_synchronizer())
+ # Clocks and PPS are now fully active!
+ self.mykonos.set_master_clock_rate(self.master_clock_rate)
+ self.init_jesd(jesdcore, args)
+ jesdcore = None # Help with garbage collection
+ # That's all that requires access to the dboard regs!
self.mykonos.start_radio()
- # We can now close the dboard regs UIO object if we want to (until we
- # re-run init(), of course)
- # Help with garbage collection:
- jesdcore = None
- dboard_ctrl_regs = None
return True
@@ -776,7 +765,6 @@ class Magnesium(DboardManagerBase):
self.current_jesd_rate = new_rate
return
-
def get_user_eeprom_data(self):
"""
Return a dict of blobs stored in the user data section of the EEPROM.
diff --git a/mpm/python/usrp_mpm/ethtable.py b/mpm/python/usrp_mpm/ethtable.py
index 9657f54c7..47b423a3a 100644
--- a/mpm/python/usrp_mpm/ethtable.py
+++ b/mpm/python/usrp_mpm/ethtable.py
@@ -25,6 +25,7 @@ from usrp_mpm.mpmlog import get_logger
from usrp_mpm.sys_utils.uio import UIO
from usrp_mpm.sys_utils.net import get_mac_addr
+
class EthDispatcherTable(object):
"""
Controls an Ethernet dispatcher table.
@@ -51,7 +52,8 @@ class EthDispatcherTable(object):
"""
self.log.debug("Setting my own IP address to `{}'".format(ip_addr))
ip_addr_int = int(netaddr.IPAddress(ip_addr))
- self.poke32(self.OWN_IP_OFFSET, ip_addr_int)
+ with self._regs.open():
+ self.poke32(self.OWN_IP_OFFSET, ip_addr_int)
def set_vita_port(self, port_value=None, port_idx=None):
"""
@@ -62,7 +64,8 @@ class EthDispatcherTable(object):
port_value = port_value or self.DEFAULT_VITA_PORT[port_idx]
assert port_idx in (0) #FIXME: Fix port_idx = 1
port_reg_addr = self.OWN_PORT_OFFSET
- self.poke32(port_reg_addr, port_value)
+ with self._regs.open():
+ self.poke32(port_reg_addr, port_value)
def set_route(self, sid, ip_addr, udp_port, mac_addr=None):
"""
@@ -102,24 +105,27 @@ class EthDispatcherTable(object):
ip_addr_int = int(netaddr.IPAddress(ip_addr))
mac_addr_int = int(netaddr.EUI(mac_addr))
sid_offset = 4 * dst_ep
+
def poke_and_trace(addr, data):
" Do a poke32() and log.trace() "
self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
addr, data
))
self.poke32(addr, data)
- poke_and_trace(
- self.SID_IP_OFFSET + sid_offset,
- ip_addr_int
- )
- poke_and_trace(
- self.SID_MAC_LO_OFFSET + sid_offset,
- mac_addr_int & 0xFFFFFFFF,
- )
- poke_and_trace(
- self.SID_PORT_MAC_HI_OFFSET + sid_offset,
- (udp_port << 16) | (mac_addr_int >> 32)
- )
+
+ with self._regs.open():
+ poke_and_trace(
+ self.SID_IP_OFFSET + sid_offset,
+ ip_addr_int
+ )
+ poke_and_trace(
+ self.SID_MAC_LO_OFFSET + sid_offset,
+ mac_addr_int & 0xFFFFFFFF,
+ )
+ poke_and_trace(
+ self.SID_PORT_MAC_HI_OFFSET + sid_offset,
+ (udp_port << 16) | (mac_addr_int >> 32)
+ )
def set_forward_policy(self, forward_eth, forward_bcast):
"""
@@ -130,6 +136,5 @@ class EthDispatcherTable(object):
self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
self.FORWARD_ETH_BCAST_OFFSET, reg_value
))
- self.poke32(self.FORWARD_ETH_BCAST_OFFSET, reg_value)
-
-
+ with self._regs.open():
+ self.poke32(self.FORWARD_ETH_BCAST_OFFSET, reg_value)
diff --git a/mpm/python/usrp_mpm/liberiotable.py b/mpm/python/usrp_mpm/liberiotable.py
index be19c7c17..cc848d570 100644
--- a/mpm/python/usrp_mpm/liberiotable.py
+++ b/mpm/python/usrp_mpm/liberiotable.py
@@ -47,14 +47,14 @@ class LiberioDispatcherTable(object):
self.poke32(addr, data)
# Poke reg for destination channel
try:
- poke_and_trace(
- 0 + 4 * sid.dst_ep,
- dma_channel,
- )
+ with self._regs.open():
+ poke_and_trace(
+ 0 + 4 * sid.dst_ep,
+ dma_channel,
+ )
except Exception as ex:
self.log.error(
"Unexpected exception while setting route: %s",
str(ex),
)
raise
-
diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py
index c73ba6ef4..28b1656e4 100644
--- a/mpm/python/usrp_mpm/periph_manager/base.py
+++ b/mpm/python/usrp_mpm/periph_manager/base.py
@@ -22,6 +22,7 @@ from __future__ import print_function
import os
from hashlib import md5
from concurrent import futures
+from time import sleep
from builtins import str
from builtins import range
from builtins import object
@@ -226,6 +227,9 @@ class PeriphManagerBase(object):
))
for overlay in requested_overlays:
dtoverlay.apply_overlay_safe(overlay)
+ # Need to wait here a second to make sure the ethernet interfaces are up
+ # TODO: Fine-tune this number, or wait for some smarter signal.
+ sleep(1)
def _init_dboards(self, override_dboard_pids=None):
diff --git a/mpm/python/usrp_mpm/periph_manager/n310.py b/mpm/python/usrp_mpm/periph_manager/n310.py
index 5d69ac956..6d194e75e 100644
--- a/mpm/python/usrp_mpm/periph_manager/n310.py
+++ b/mpm/python/usrp_mpm/periph_manager/n310.py
@@ -267,6 +267,8 @@ class n310(PeriphManagerBase):
# This file will always contain the current image, regardless of SFP type,
# dboard, etc. The host is responsible for providing a compatible image
# for the N310's current setup.
+ # Label for the mboard UIO
+ mboard_regs_label = "mboard-regs"
# Override the list of updateable components
updateable_components = {
'fpga': {
@@ -312,9 +314,7 @@ class n310(PeriphManagerBase):
sure to catch it.
"""
# Init Mboard Regs
- self.mboard_regs = self._init_mboard_regs()
- self.log.trace("Motherboard-register UIO object successfully generated!")
- self.mboard_regs_control = MboardRegsControl(self.mboard_regs, self.log)
+ self.mboard_regs_control = MboardRegsControl(self.mboard_regs_label, self.log)
self.mboard_regs_control.get_git_hash()
# Init peripherals
self.log.trace("Initializing TCA6424 port expander controls...")
@@ -350,13 +350,6 @@ class n310(PeriphManagerBase):
# Init complete.
self.log.info("mboard info: {}".format(self.mboard_info))
- def _init_mboard_regs(self):
- " Create a UIO object to talk to mboard regs "
- return UIO(
- label="mboard-regs",
- read_only=False
- )
-
def _init_ref_clock_and_time(self, default_args):
"""
Initialize clock and time sources. After this function returns, the
@@ -865,9 +858,12 @@ class MboardRegsControl(object):
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
- def __init__(self, regs, log):
+ def __init__(self, label, log):
self.log = log
- self.regs = regs
+ self.regs = UIO(
+ label=label,
+ read_only=False
+ )
self.poke32 = self.regs.poke32
self.peek32 = self.regs.peek32
@@ -875,7 +871,8 @@ class MboardRegsControl(object):
"""
Returns the GIT hash for the FPGA build.
"""
- git_hash = self.peek32(self.MB_GIT_HASH)
+ with self.regs.open():
+ git_hash = self.peek32(self.MB_GIT_HASH)
self.log.trace("FPGA build GIT Hash: 0x{:08X}".format(git_hash))
return git_hash
@@ -900,11 +897,11 @@ class MboardRegsControl(object):
pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_GPSDO
else:
assert False
-
- reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFFF0; # clear lowest nibble
- reg_val = reg_val | (pps_sel_val & 0xF) # set lowest nibble
- self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
- self.poke32(self.MB_CLOCK_CTRL, reg_val)
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFFF0; # clear lowest nibble
+ reg_val = reg_val | (pps_sel_val & 0xF) # set lowest nibble
+ self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
+ self.poke32(self.MB_CLOCK_CTRL, reg_val)
def enable_pps_out(self, enable):
"""
@@ -912,11 +909,12 @@ class MboardRegsControl(object):
"""
self.log.trace("%s PPS/Trig output!", "Enabling" if enable else "Disabling")
mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN)
- reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask # mask the bit to clear it
- if enable:
- reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN) # set the bit if desired
- self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
- self.poke32(self.MB_CLOCK_CTRL, reg_val)
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask # mask the bit to clear it
+ if enable:
+ reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN) # set the bit if desired
+ self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
+ self.poke32(self.MB_CLOCK_CTRL, reg_val)
def reset_meas_clk_mmcm(self, reset=True):
"""
@@ -924,18 +922,20 @@ class MboardRegsControl(object):
"""
self.log.trace("%s measurement clock MMCM reset...", "Asserting" if reset else "Clearing")
mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET)
- reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask # mask the bit to clear it
- if reset:
- reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET) # set the bit if desired
- self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
- self.poke32(self.MB_CLOCK_CTRL, reg_val)
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask # mask the bit to clear it
+ if reset:
+ reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET) # set the bit if desired
+ self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
+ self.poke32(self.MB_CLOCK_CTRL, reg_val)
def get_meas_clock_mmcm_lock(self):
"""
Check the status of the MMCM for the measurement clock in the FPGA TDC.
"""
mask = 0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_LOCKED
- reg_val = self.peek32(self.MB_CLOCK_CTRL)
+ with self.regs.open():
+ reg_val = self.peek32(self.MB_CLOCK_CTRL)
locked = (reg_val & mask) > 0
if not locked:
self.log.warning("Measurement clock MMCM reporting unlocked. MB_CLOCK_CTRL "
diff --git a/mpm/python/usrp_mpm/rpc_server.py b/mpm/python/usrp_mpm/rpc_server.py
index d645cb396..1f5007e9c 100644
--- a/mpm/python/usrp_mpm/rpc_server.py
+++ b/mpm/python/usrp_mpm/rpc_server.py
@@ -306,18 +306,9 @@ class MPMServer(RPCServer):
try:
self.log.trace("Reset after updating component? {}".format(reset_now))
if reset_now:
- # TODO remove this hellspawn
- self.log.error("Exiting now because you know we have this " \
- "bug we haven't fixed yet. Just respawn MPM "\
- "manually for now. Sorry about this. Ahem. " \
- "Please hit Ctrl-C now.")
- self.log.critical("Seriously, hit Ctrl-C.")
- assert False
- # End of evil code (at least, *this* evil code)
self.reset_mgr()
self.log.debug("Reset the periph manager")
except Exception as ex:
- raise # This is also part of the evilness TODO remove
self.log.error(
"Error in update_component while resetting: {}".format(
ex
diff --git a/mpm/python/usrp_mpm/sys_utils/uio.py b/mpm/python/usrp_mpm/sys_utils/uio.py
index c07227776..acbe2cc4f 100644
--- a/mpm/python/usrp_mpm/sys_utils/uio.py
+++ b/mpm/python/usrp_mpm/sys_utils/uio.py
@@ -18,17 +18,27 @@
Access to UIO mapped memory.
"""
-import struct
import os
-import mmap
-from builtins import hex
from builtins import object
+from contextlib import contextmanager
import pyudev
+import usrp_mpm.libpyusrp_periphs as lib
from usrp_mpm.mpmlog import get_logger
UIO_SYSFS_BASE_DIR = '/sys/class/uio'
UIO_DEV_BASE_DIR = '/dev'
+
+@contextmanager
+def open_uio(label=None, path=None, length=None, read_only=True, offset=None):
+ """Convenience function for creating a UIO object.
+ Use this like you would open() for a file"""
+ uio_obj = UIO(label, path, length, read_only, offset)
+ uio_obj.open()
+ yield uio_obj
+ uio_obj.close()
+
+
def get_all_uio_devs():
"""
Return a list of all uio devices. Will look something like
@@ -43,6 +53,7 @@ def get_all_uio_devs():
# Typically means UIO devices
return []
+
def get_uio_map_info(uio_dev, map_num):
"""
Returns all the map info for a given UIO device and map number.
@@ -64,6 +75,7 @@ def get_uio_map_info(uio_dev, map_num):
map_info[info_file] = map_info_value
return map_info
+
def find_uio_device(label, logger=None):
"""
Given a label, returns a tuple (uio_device, map_info).
@@ -85,6 +97,7 @@ def find_uio_device(label, logger=None):
logger.warning("Found no matching UIO device for label `{0}'".format(label))
return None, None
+
class UIO(object):
"""
Provides peek/poke interfaces for uio-mapped memory.
@@ -123,35 +136,30 @@ class UIO(object):
self.log.error("Could not find a UIO device for label {0}".format(label))
raise RuntimeError("Could not find a UIO device for label {0}".format(label))
self._read_only = read_only
- self.log.trace("Opening UIO device file {}...".format(self._path))
- self._fd = os.open(self._path, os.O_RDONLY if read_only else os.O_RDWR)
- self.log.trace("Calling mmap({fd}, length={length}, offset={offset})".format(
- fd=self._fd, length=hex(length), offset=hex(offset)
- ))
- self._mm = mmap.mmap(
- self._fd,
- length,
- flags=mmap.MAP_SHARED,
- prot=mmap.PROT_READ | (0 if read_only else mmap.PROT_WRITE),
- offset=offset,
- )
-
- def __del__(self):
- """
- Destructor needs to close the uio-mapped memory
- """
- try:
- self._mm.close()
- os.close(self._fd)
- except:
- self.log.warning("Failed to properly destruct UIO object.")
- pass
+ # Our UIO objects are managed in C++ land, which gives us more granular control over
+ # opening and closing
+ self._uio = lib.types.mmap_regs_iface(self._path, length, offset, self._read_only, False)
+
+ def __enter__(self):
+ self.open()
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.close()
+ return exc_type is None
+
+ def open(self):
+ self._uio.open()
+ return self
+
+ def close(self):
+ self._uio.close()
def peek32(self, addr):
"""
Returns the 32-bit value starting at address addr as an integer
"""
- return struct.unpack('@I', self._mm[addr:addr+4])[0]
+ return self._uio.peek32(addr)
def poke32(self, addr, val):
"""
@@ -160,8 +168,4 @@ class UIO(object):
A value that exceeds 32 bits will be truncated to 32 bits.
"""
assert not self._read_only
- self._mm[addr:addr+4] = struct.pack(
- '@I',
- (val & 0xFFFFFFFF),
- )
-
+ return self._uio.poke32(addr, val)