mpm: x4xx: Move MB CPLD creation to factory

Main changes:
- x4xx_mb_cpld.MboardCPLD is renamed to X4xxMboardCPLD and is now a base
  class. Specific implementations of the MB CPLD require derived classes
  and have to provide their corresponding signature.
- In x4xx.py, we don't init the MB CPLD and then assert we have
  a specific signature. Instead, we init the MB CPLD, and choose
  a derived class based on the signature. If there is no such class,
  then the same error is generated as before (by itself, this means
  there is no behavioural change).
- The MB CPLD image for the X410 (ZBX daughterboards) is moved to
  a derived class X410MboardCPLD.
- New: The ZBX daughterboard driver verifies that the MB CPLD image is
  in fact compatible with the daughterboard. For this, the MB CPLD
  control classes require a COMPATIBLE_DB_PIDS attribute.

By itself, this change has no behavioural- or API changes. However, it
allows easily slotting in new CPLD images with different signatures.
Without further modifications, it does not allow *any* CPLD image
though: The PS API (e.g., enable/disable daughterboards, CMI status,
etc.) remain the same.
This commit is contained in:
Martin Braun 2022-10-28 11:23:55 +02:00 committed by Aki Tomita
parent 4b007c42b3
commit 01ccb69459
3 changed files with 82 additions and 34 deletions

View file

@ -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):
"""

View file

@ -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.

View file

@ -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