diff --git a/mpm/python/usrp_mpm/dboard_manager/x4xx_db.py b/mpm/python/usrp_mpm/dboard_manager/x4xx_db.py index cdeeb7949..f74506200 100644 --- a/mpm/python/usrp_mpm/dboard_manager/x4xx_db.py +++ b/mpm/python/usrp_mpm/dboard_manager/x4xx_db.py @@ -56,6 +56,7 @@ class X4xxDbMixin: rev_compat = kwargs.get('rev_compat') if 'rev_compat' in kwargs else \ self.get_eeprom()['rev_compat'] self._assert_rev_compatibility(rev_compat) + self._assert_mb_cpld_compatibility(self.db_iface.mboard.cpld_control) ########################################################################### # Init helpers @@ -87,6 +88,20 @@ class X4xxDbMixin: self.log.error(err) raise RuntimeError(err) + def _assert_mb_cpld_compatibility(self, mb_cpld_ctrl): + """ + Verify that the MB CPLD is compatible with this daughterboard. + + Throws a RuntimeError() if that's not the case. + """ + if not any(pid in self.pids for pid in mb_cpld_ctrl.COMPATIBLE_DB_PIDS): + err = \ + f"This {self.product_name} daughterboard is not compatible with " \ + f"the motherboard CPLD image (CPLD signature: {mb_cpld_ctrl.SIGNATURE:X}). " \ + f"Please update the motherboard CPLD image." + self.log.error(err) + raise RuntimeError(err) + @no_rpc def enable_base_power(self, enable=True): """ diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx.py b/mpm/python/usrp_mpm/periph_manager/x4xx.py index b6574d38b..47ce83905 100644 --- a/mpm/python/usrp_mpm/periph_manager/x4xx.py +++ b/mpm/python/usrp_mpm/periph_manager/x4xx.py @@ -30,7 +30,7 @@ from usrp_mpm.periph_manager.x4xx_periphs import CtrlportRegs from usrp_mpm.periph_manager.x4xx_dio_control import DioControl from usrp_mpm.periph_manager.x4xx_periphs import QSFPModule from usrp_mpm.periph_manager.x4xx_periphs import get_temp_sensor -from usrp_mpm.periph_manager.x4xx_mb_cpld import MboardCPLD +from usrp_mpm.periph_manager.x4xx_mb_cpld import make_mb_cpld_ctrl from usrp_mpm.periph_manager.x4xx_clk_aux import ClockingAuxBrdControl from usrp_mpm.periph_manager.x4xx_clk_mgr import X4xxClockMgr from usrp_mpm.periph_manager.x4xx_gps_mgr import X4xxGPSMgr @@ -462,10 +462,9 @@ class x4xx(ZynqComponents, PeriphManagerBase): # Init CPLD before talking to clocking ICs cpld_spi_node = dt_symbol_get_spidev('mb_cpld') - self.cpld_control = MboardCPLD(cpld_spi_node, self.log) - self.cpld_control.check_signature() - self.cpld_control.check_compat_version() - self.cpld_control.trace_git_hash() + # This factory function will check signature and compat-rev, and + # therefore could throw if the CPLD is not compatible. + self.cpld_control = make_mb_cpld_ctrl(cpld_spi_node, self.log) self._assert_rfdc_powered() # Init clocking after CPLD as the SPLL communication is relying on it. diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py b/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py index 7a9b2cc7e..1399446ae 100644 --- a/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py @@ -7,12 +7,17 @@ X4xx motherboard CPLD control """ +import sys +import inspect from usrp_mpm import lib # Pulls in everything from C++-land from usrp_mpm.mpmutils import parse_encoded_git_hash +from usrp_mpm.dboard_manager import ZBX -class MboardCPLD: +class X4xxMboardCPLD: """ - Control for the CPLD. + Base class for the CPLD control + + Derive from this class for a specific implementation of the X4xx MB CPLD. """ # pylint: disable=bad-whitespace SIGNATURE_OFFSET = 0x0000 @@ -26,10 +31,10 @@ class MboardCPLD: SERIAL_NO_HI_OFFSET = 0x0038 CMI_OFFSET = 0x003C - # change these revisions only on breaking changes - OLDEST_REQ_COMPAT_REV = 0x20122114 - REQ_COMPAT_REV = 0x20122114 - SIGNATURE = 0x0A522D27 + # These need to be filled out by derived classes + OLDEST_REQ_COMPAT_REV = None + REQ_COMPAT_REV = None + SIGNATURE = None # Bit fields in DB_ENABLE_OFFSET DB0_CLOCK_ENABLED = 1 << 0 @@ -47,19 +52,13 @@ class MboardCPLD: RELEASE_RST_DB1 = 1 << 17 ASSERT_RST_DB0 = 1 << 20 ASSERT_RST_DB1 = 1 << 21 + + COMPATIBLE_DB_PIDS = [] # pylint: enable=bad-whitespace - def __init__(self, spi_dev_node, log): - self.log = log.getChild("CPLD") - self.regs = lib.spi.make_spidev_regs_iface( - spi_dev_node, - 1000000, # Speed (Hz) - 0, # SPI mode - 32, # Addr shift - 0, # Data shift - 0, # Read flag - 1<<47 # Write flag - ) + def __init__(self, regs, log): + self.log = log + self.regs = regs self.poke32 = self.regs.poke32 self.peek32 = self.regs.peek32 @@ -87,6 +86,7 @@ class MboardCPLD: def enable_daughterboard(self, db_id, enable=True): """ Enable or disable clock forwarding to a given DB """ + assert db_id in (0, 1) if db_id == 0: release_reset = self.RELEASE_RST_DB0 assert_reset = self.ASSERT_RST_DB0 @@ -119,18 +119,6 @@ class MboardCPLD: value = (value | clk_disable) & (~clk_enable) self.poke32(self.DB_ENABLE_OFFSET, value) - def check_signature(self): - """ - Assert that the CPLD signature is correct. If the CPLD 'signature' - register returns something unexpectected, throws a RuntimeError. - """ - read_signature = self.peek32(self.SIGNATURE_OFFSET) - if self.SIGNATURE != read_signature: - self.log.error('MB PS CPLD signature {:X} does not match ' - 'expected value {:X}'.format(read_signature, self.SIGNATURE)) - raise RuntimeError('MB PS CPLD signature {:X} does not match ' - 'expected value {:X}'.format(read_signature, self.SIGNATURE)) - def check_compat_version(self): """ Check oldest compatible revision offset of HW against required revision. @@ -205,3 +193,49 @@ class MboardCPLD: Return true if upstream CMI device was found. """ return bool(self.peek32(self.CMI_OFFSET)) + + +class X410MboardCPLD(X4xxMboardCPLD): + """ + MB CPLD Image for USRP X410 (which means there are ZBX daughterboards + installed). + """ + # pylint: disable=bad-whitespace + # change these revisions only on breaking changes + OLDEST_REQ_COMPAT_REV = 0x20122114 + REQ_COMPAT_REV = 0x20122114 + SIGNATURE = 0x0A522D27 + COMPATIBLE_DB_PIDS = ZBX.pids + # pylint: enable=bad-whitespace + + +def make_mb_cpld_ctrl(spi_dev_node, log): + """ + Factory function for the X4xx MB CPLD core + """ + log = log.getChild("CPLD") + regs = lib.spi.make_spidev_regs_iface( + spi_dev_node, + 1000000, # Speed (Hz) + 0, # SPI mode + 32, # Addr shift + 0, # Data shift + 0, # Read flag + 1<<47 # Write flag + ) + cpld_signature = regs.peek32(X4xxMboardCPLD.SIGNATURE_OFFSET) + log.trace("Found MB CPLD signature: %x", cpld_signature) + def _map_sig_to_class(signature): + for name, obj in sys.modules[__name__].__dict__.items(): + if inspect.isclass(obj) and \ + issubclass(obj, X4xxMboardCPLD) and \ + getattr(obj, 'SIGNATURE') == signature: + log.debug("Found MB CPLD control class: %s", name) + return obj + raise RuntimeError( + "Unable to find a MB CPLD controller for CPLD with signature " + f"{cpld_signature:X}!") + cpld_control = _map_sig_to_class(cpld_signature)(regs, log) + cpld_control.check_compat_version() + cpld_control.trace_git_hash() + return cpld_control