2017-03-22 02:46:50 +00:00
|
|
|
#
|
2018-02-20 00:53:15 +00:00
|
|
|
# Copyright 2017 Ettus Research, a National Instruments Company
|
2017-03-22 02:46:50 +00:00
|
|
|
#
|
2018-02-20 00:53:15 +00:00
|
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
2017-03-22 02:46:50 +00:00
|
|
|
#
|
|
|
|
|
"""
|
|
|
|
|
dboard base implementation module
|
|
|
|
|
"""
|
2017-04-27 00:25:23 +00:00
|
|
|
|
2017-06-01 02:02:58 +00:00
|
|
|
from builtins import object
|
2017-05-11 01:34:19 +00:00
|
|
|
from six import iteritems
|
2017-12-12 17:59:50 +00:00
|
|
|
from usrp_mpm.mpmlog import get_logger
|
2018-01-03 21:13:18 +00:00
|
|
|
from usrp_mpm.mpmutils import to_native_str
|
2017-03-22 02:46:50 +00:00
|
|
|
|
2017-03-28 01:03:52 +00:00
|
|
|
class DboardManagerBase(object):
|
2017-03-22 02:46:50 +00:00
|
|
|
"""
|
2017-05-10 19:07:14 +00:00
|
|
|
Base class for daughterboard controls
|
2017-03-22 02:46:50 +00:00
|
|
|
"""
|
2017-05-10 19:07:14 +00:00
|
|
|
#########################################################################
|
|
|
|
|
# Overridables
|
|
|
|
|
#
|
|
|
|
|
# These values are meant to be overridden by the according subclasses
|
|
|
|
|
#########################################################################
|
|
|
|
|
# Very important: A list of PIDs that apply to the current device. Must be
|
|
|
|
|
# list, even if there's only one entry.
|
|
|
|
|
pids = []
|
2021-06-04 06:27:50 +00:00
|
|
|
# tuple of id and name of the first revision,
|
|
|
|
|
# id and name of revisions are consecutive (2, B), (3, C), ...
|
|
|
|
|
first_revision = (1, 'A')
|
2017-09-29 20:58:43 +00:00
|
|
|
# See PeriphManager.mboard_sensor_callback_map for a description.
|
|
|
|
|
rx_sensor_callback_map = {}
|
|
|
|
|
# See PeriphManager.mboard_sensor_callback_map for a description.
|
|
|
|
|
tx_sensor_callback_map = {}
|
2017-05-11 01:34:19 +00:00
|
|
|
# A dictionary that maps chips or components to chip selects for SPI.
|
|
|
|
|
# If this is given, a dictionary called self._spi_nodes is created which
|
|
|
|
|
# maps these keys to actual spidev paths. Also throws a warning/error if
|
|
|
|
|
# the SPI configuration is invalid.
|
|
|
|
|
spi_chipselect = {}
|
2017-06-03 08:33:54 +00:00
|
|
|
### End of overridables #################################################
|
2017-03-28 01:03:52 +00:00
|
|
|
|
2017-05-10 19:07:14 +00:00
|
|
|
def __init__(self, slot_idx, **kwargs):
|
2017-04-20 01:45:29 +00:00
|
|
|
self.log = get_logger('dboardManager')
|
2017-05-10 19:07:14 +00:00
|
|
|
self.slot_idx = slot_idx
|
2017-10-12 17:47:07 +00:00
|
|
|
if 'eeprom_md' not in kwargs:
|
2018-03-06 23:16:22 +00:00
|
|
|
self.log.debug("No EEPROM metadata given!")
|
2017-10-12 17:47:07 +00:00
|
|
|
# In C++, we can only handle dicts if all the values are of the
|
|
|
|
|
# same type. So we must convert them all to strings here:
|
|
|
|
|
self.device_info = {
|
2018-01-03 21:13:18 +00:00
|
|
|
key: to_native_str(kwargs.get('eeprom_md', {}).get(key, 'n/a'))
|
2017-10-12 17:47:07 +00:00
|
|
|
for key in ('pid', 'serial', 'rev', 'eeprom_version')
|
|
|
|
|
}
|
|
|
|
|
self.log.trace("Dboard device info: `{}'".format(self.device_info))
|
2017-11-17 19:13:42 +00:00
|
|
|
self._spi_nodes = self._init_spi_nodes(
|
|
|
|
|
kwargs.get('spi_nodes', []),
|
|
|
|
|
self.spi_chipselect
|
|
|
|
|
)
|
|
|
|
|
self.log.debug("spidev device node map: {}".format(self._spi_nodes))
|
2017-05-11 01:34:19 +00:00
|
|
|
|
|
|
|
|
|
2017-11-17 19:13:42 +00:00
|
|
|
def _init_spi_nodes(self, spi_devices, chip_select_map):
|
2017-05-11 01:34:19 +00:00
|
|
|
"""
|
2017-11-17 19:13:42 +00:00
|
|
|
Populates a spi_nodes dictionary.
|
2017-05-12 21:27:59 +00:00
|
|
|
Note that this won't instantiate any spidev objects, it'll just map
|
2017-11-17 19:13:42 +00:00
|
|
|
keys from chip_select_map to spidev nodes, and do a sanity check
|
2017-05-12 21:27:59 +00:00
|
|
|
that enough nodes are available.
|
2017-05-11 01:34:19 +00:00
|
|
|
"""
|
2018-05-08 22:54:26 +00:00
|
|
|
if len(spi_devices) < len(set(chip_select_map.values())):
|
2017-11-17 19:13:42 +00:00
|
|
|
self.log.error("Expected {0} spi devices, found {1}".format(
|
2018-05-08 22:54:26 +00:00
|
|
|
len(set(chip_select_map.values())), len(spi_devices),
|
2017-05-11 01:34:19 +00:00
|
|
|
))
|
2017-11-17 19:13:42 +00:00
|
|
|
self.log.error("Not enough SPI devices found.")
|
|
|
|
|
return {}
|
|
|
|
|
return {
|
|
|
|
|
spi_device: spi_devices[chip_select]
|
|
|
|
|
for spi_device, chip_select in iteritems(chip_select_map)
|
|
|
|
|
}
|
2017-03-22 02:46:50 +00:00
|
|
|
|
2017-05-12 21:27:59 +00:00
|
|
|
def init(self, args):
|
|
|
|
|
"""
|
|
|
|
|
Run the dboard initialization. This typically happens at the beginning
|
|
|
|
|
of a UHD session.
|
|
|
|
|
|
2017-06-03 08:33:54 +00:00
|
|
|
Must be overridden. Must return True/False on success/failure.
|
2017-05-12 21:27:59 +00:00
|
|
|
|
|
|
|
|
args -- A dictionary of arbitrary settings that can be used by the
|
|
|
|
|
dboard code. Similar to device args for UHD.
|
|
|
|
|
"""
|
|
|
|
|
raise NotImplementedError("DboardManagerBase::init() not implemented!")
|
|
|
|
|
|
|
|
|
|
def deinit(self):
|
|
|
|
|
"""
|
|
|
|
|
Power down the dboard. Does not have be implemented. If it does, it
|
|
|
|
|
needs to be safe to call multiple times.
|
|
|
|
|
"""
|
2017-11-16 06:13:30 +00:00
|
|
|
self.log.debug("deinit() called, but not implemented.")
|
2017-05-12 21:27:59 +00:00
|
|
|
|
2021-06-04 06:27:50 +00:00
|
|
|
def tear_down(self):
|
|
|
|
|
"""
|
|
|
|
|
Tear down all members that need to be specially handled before
|
|
|
|
|
deconstruction.
|
|
|
|
|
"""
|
|
|
|
|
pass
|
|
|
|
|
|
2017-03-22 02:46:50 +00:00
|
|
|
def get_serial(self):
|
2017-05-12 21:27:59 +00:00
|
|
|
"""
|
2018-06-19 00:37:11 +00:00
|
|
|
Return this daughterboard's serial number as a string. Will return an
|
|
|
|
|
empty string if no serial can be found.
|
2017-05-12 21:27:59 +00:00
|
|
|
"""
|
|
|
|
|
return self.device_info.get("serial", "")
|
2017-05-02 21:31:48 +00:00
|
|
|
|
2021-06-04 06:27:50 +00:00
|
|
|
def get_revision(self):
|
|
|
|
|
"""
|
|
|
|
|
Return this daughterboard's revision number as integer. Will return
|
|
|
|
|
-1 if no revision can be found or revision is not an integer
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
return int(self.device_info.get('rev', '-1'))
|
|
|
|
|
except ValueError:
|
|
|
|
|
return -1
|
|
|
|
|
|
|
|
|
|
def get_revision_string(self):
|
|
|
|
|
"""
|
|
|
|
|
Converts revision number to string.
|
|
|
|
|
"""
|
|
|
|
|
return chr(ord(self.first_revision[1])
|
|
|
|
|
+ self.get_revision()
|
|
|
|
|
- self.first_revision[0])
|
|
|
|
|
|
|
|
|
|
##########################################################################
|
|
|
|
|
# Clocking
|
|
|
|
|
##########################################################################
|
|
|
|
|
def reset_clock(self, value):
|
|
|
|
|
"""
|
|
|
|
|
Called when the motherboard is reconfiguring its clocks.
|
|
|
|
|
"""
|
|
|
|
|
pass
|
|
|
|
|
|
2018-10-10 06:26:15 +00:00
|
|
|
def update_ref_clock_freq(self, freq, **kwargs):
|
2017-05-02 21:31:48 +00:00
|
|
|
"""
|
|
|
|
|
Call this function if the frequency of the reference clock changes.
|
|
|
|
|
"""
|
|
|
|
|
self.log.warning("update_ref_clock_freq() called but not implemented")
|
2017-09-29 20:58:43 +00:00
|
|
|
|
2017-11-17 00:41:32 +00:00
|
|
|
##########################################################################
|
|
|
|
|
# Sensors
|
|
|
|
|
##########################################################################
|
|
|
|
|
def get_sensors(self, direction, chan=0):
|
2017-09-29 20:58:43 +00:00
|
|
|
"""
|
|
|
|
|
Return a list of RX daughterboard sensor names.
|
|
|
|
|
|
|
|
|
|
direction needs to be either RX or TX.
|
|
|
|
|
"""
|
|
|
|
|
if direction.lower() == 'rx':
|
|
|
|
|
return list(self.rx_sensor_callback_map.keys())
|
|
|
|
|
else:
|
|
|
|
|
return list(self.tx_sensor_callback_map.keys())
|
|
|
|
|
|
2017-11-17 00:41:32 +00:00
|
|
|
def get_sensor(self, direction, sensor_name, chan=0):
|
2017-09-29 20:58:43 +00:00
|
|
|
"""
|
|
|
|
|
Return a dictionary that represents the sensor values for a given
|
|
|
|
|
sensor. If the requested sensor sensor_name does not exist, throw an
|
|
|
|
|
exception. direction is either RX or TX.
|
|
|
|
|
|
|
|
|
|
See PeriphManager.get_mb_sensor() for a description of the return value
|
|
|
|
|
format.
|
|
|
|
|
"""
|
|
|
|
|
callback_map = \
|
2017-10-10 00:44:02 +00:00
|
|
|
self.rx_sensor_callback_map if direction.lower() == 'rx' \
|
|
|
|
|
else self.tx_sensor_callback_map
|
2017-09-29 20:58:43 +00:00
|
|
|
if sensor_name not in callback_map:
|
|
|
|
|
error_msg = "Was asked for non-existent sensor `{}'.".format(
|
|
|
|
|
sensor_name
|
|
|
|
|
)
|
|
|
|
|
self.log.error(error_msg)
|
|
|
|
|
raise RuntimeError(error_msg)
|
|
|
|
|
return getattr(
|
2018-01-12 01:09:37 +00:00
|
|
|
self, callback_map.get(sensor_name)
|
2017-11-17 00:41:32 +00:00
|
|
|
)(chan)
|
2017-09-29 20:58:43 +00:00
|
|
|
|