diff options
| -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)  | 
