diff options
author | Brent Stapleton <brent.stapleton@ettus.com> | 2017-11-29 17:05:41 -0800 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:05:58 -0800 |
commit | 0bf22a3e57dab4af3bfc66dbae999d3d9d11341b (patch) | |
tree | fd8af6de9c1578124f8cbd5d8c21052786d24913 /mpm | |
parent | 3cff6298cea5f3699aa5db5921cd6933f557a034 (diff) | |
download | uhd-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.py | 143 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/magnesium.py | 44 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/ethtable.py | 39 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/liberiotable.py | 10 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/base.py | 4 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n310.py | 58 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/rpc_server.py | 9 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/sys_utils/uio.py | 68 |
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) |