File: x4xx_db.py

package info (click to toggle)
uhd 4.9.0.0%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 184,180 kB
  • sloc: cpp: 262,887; python: 112,011; ansic: 102,670; vhdl: 57,031; tcl: 19,924; xml: 8,581; makefile: 3,028; sh: 2,812; pascal: 230; javascript: 120; csh: 94; asm: 20; perl: 11
file content (167 lines) | stat: -rw-r--r-- 6,511 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#
# Copyright 2022 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
Mixin class for daughterboard classes that live on a X4xx motherboard
"""

from usrp_mpm.mpmlog import get_logger
from usrp_mpm import tlv_eeprom
from usrp_mpm.sys_utils.udev import get_eeprom_paths_by_symbol
from usrp_mpm.rpc_utils import no_rpc

# pylint: disable=too-few-public-methods
class EepromTagMap:
    """
    Defines the tagmap for EEPROMs matching this magic.
    The tagmap is a dictionary mapping an 8-bit tag to a NamedStruct instance.
    The canonical list of tags and the binary layout of the associated structs
    is defined in mpm/tools/tlv_eeprom/usrp_eeprom.h. Only the subset relevant
    to MPM are included below.
    """
    magic = 0x55535250
    tagmap = {
        # 0x10: usrp_eeprom_board_info
        0x10: tlv_eeprom.NamedStruct('< H H H 7s 1x',
                                     ['pid', 'rev', 'rev_compat', 'serial']),
    }

class X4xxDbMixin:
    """
    Mixin class for daughterboards that live in an X4xx motherboard.
    """
    # Make sure to populated this in the base class
    DBOARD_SUPPORTED_COMPAT_REVS = ()

    def __init__(self, product_name, slot_idx, **kwargs):
        self.log = get_logger(f"{product_name}-{slot_idx}")
        self.log.trace("Initializing %s daughterboard, slot index %d",
                       product_name, slot_idx)
        super().__init__(slot_idx, **kwargs)
        self.eeprom_symbol = f"db{slot_idx}_eeprom"
        self.product_name = product_name

        # Get MB interface
        if 'db_iface' not in kwargs:
            self.log.error("Required DB Iface was not provided!")
            raise RuntimeError("Required DB Iface was not provided!")
        self.db_iface = kwargs['db_iface']

        self.enable_base_power()

        assert self.DBOARD_SUPPORTED_COMPAT_REVS
        # This allows overriding the rev_compat from the EEPROM
        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
    ###########################################################################
    @no_rpc
    def get_eeprom(self):
        """
        Return the eeprom data.
        """
        path = get_eeprom_paths_by_symbol(self.eeprom_symbol)[self.eeprom_symbol]
        eeprom, _ = tlv_eeprom.read_eeprom(
            path, EepromTagMap.tagmap, EepromTagMap.magic, None)
        return eeprom

    def _assert_rev_compatibility(self, rev_compat):
        """
        Check the daughterboard hardware revision compatibility with this driver.

        Throws a RuntimeError() if this version of MPM does not recognize the
        hardware.

        Note: The CPLD image version is checked separately.
        """
        if rev_compat not in self.DBOARD_SUPPORTED_COMPAT_REVS:
            err = \
                f"This MPM version is not compatible with this {self.product_name}" \
                f"daughterboard. Found rev_compat value: 0x{rev_compat:02x}. " \
                "Please update your MPM version to support this daughterboard revision."
            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):
        """
        Enables or disables power to the DB.
        """
        if enable:
            self.db_iface.enable_daughterboard(enable=True)
            if not self.db_iface.check_enable_daughterboard():
                self.db_iface.enable_daughterboard(enable=False)
                err_msg = f"{self.product_name} {self.slot_idx} power up failed"
                self.log.error(err_msg)
                raise RuntimeError(err_msg)
        else: # disable
            # Removing power from the CPLD will set all the output pins to open and the
            # supplies default to disabled on power up.
            self.db_iface.enable_daughterboard(enable=False)
            if self.db_iface.check_enable_daughterboard():
                err_msg = f"{self.product_name} {self.slot_idx} power down failed"
                self.log.error(err_msg)

    ###########################################################################
    # Clocking and RFDC interface
    ###########################################################################
    def get_rfdc_rate_sensor(self, _):
        """
        Return the RFDC rate (the ADC/DAC converter rate) of this daughterboard's
        converters as a sensor value.
        """
        return {
            'name': 'rfdc_rate',
            'type': 'REALNUM',
            'unit': 'Hz',
            'value': str(self.get_dboard_sample_rate()),
        }

    ###########################################################################
    # Clocking and RFDC interface
    ###########################################################################
    def enable_iq_swap(self, enable, trx, channel):
        """
        Turn on IQ swapping in the RFDC
        """
        self.db_iface.enable_iq_swap(enable, trx, channel)

    def get_dboard_sample_rate(self):
        """
        Return the RFDC rate. For ZBX, this is usually a big number in the
        3 GHz range.
        """
        return self.db_iface.get_sample_rate()

    def get_dboard_prc_rate(self):
        """
        Return the PRC rate. The CPLD and LOs are clocked with this.
        """
        return self.db_iface.get_prc_rate()

    def get_master_clock_rate(self):
        """
        Return the master clock rate. This is the rate that UHD cares about,
        in the 125-500 MHz range for X410/ZBX.
        """
        return self.db_iface.mboard.clk_mgr.get_master_clock_rate(self.slot_idx)