uhd/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py
Lars Amsel 2a575bf9b5 uhd: Add support for the USRP X410
Co-authored-by: Lars Amsel <lars.amsel@ni.com>
Co-authored-by: Michael Auchter <michael.auchter@ni.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Cristina Fuentes <cristina.fuentes-curiel@ni.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Virendra Kakade <virendra.kakade@ni.com>
Co-authored-by: Lane Kolbly <lane.kolbly@ni.com>
Co-authored-by: Max Köhler <max.koehler@ni.com>
Co-authored-by: Andrew Lynch <andrew.lynch@ni.com>
Co-authored-by: Grant Meyerhoff <grant.meyerhoff@ni.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Thomas Vogel <thomas.vogel@ni.com>
2021-06-10 12:01:53 -05:00

204 lines
7.8 KiB
Python

#
# Copyright 2021 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
X4xx motherboard CPLD control
"""
from usrp_mpm import lib # Pulls in everything from C++-land
def parse_encoded_git_hash(encoded):
git_hash = encoded & 0x0FFFFFFF
tree_dirty = ((encoded & 0xF0000000) > 0)
dirtiness_qualifier = 'dirty' if tree_dirty else 'clean'
return (git_hash, dirtiness_qualifier)
class MboardCPLD:
"""
Control for the CPLD.
"""
# pylint: disable=bad-whitespace
SIGNATURE_OFFSET = 0x0000
COMPAT_REV_OFFSET = 0x0004
OLDEST_COMPAT_REV_OFFSET = 0x0008
GIT_HASH_OFFSET = 0x0010
DB_ENABLE_OFFSET = 0x0020
SERIAL_NO_LO_OFFSET = 0x0034
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
PLL_REF_CLOCK_ENABLED = 1 << 2
ENABLE_CLK_DB0 = 1 << 8
ENABLE_CLK_DB1 = 1 << 9
ENABLE_PRC = 1 << 10
DISABLE_CLK_DB0 = 1 << 12
DISABLE_CLK_DB1 = 1 << 13
DISABLE_PRC = 1 << 14
RELEASE_RST_DB0 = 1 << 16
RELEASE_RST_DB1 = 1 << 17
ASSERT_RST_DB0 = 1 << 20
ASSERT_RST_DB1 = 1 << 21
# 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
)
self.poke32 = self.regs.poke32
self.peek32 = self.regs.peek32
def enable_pll_ref_clk(self, enable=True):
"""
Enables or disables the PLL reference clock.
This makes no assumptions on the prior state of the clock. It does check
if the clock-enable was successful by polling the CPLD, and throws if
not.
"""
def check_pll_enabled():
return self.peek32(self.DB_ENABLE_OFFSET) & self.PLL_REF_CLOCK_ENABLED
if enable:
self.poke32(self.DB_ENABLE_OFFSET, self.ENABLE_PRC)
if not check_pll_enabled():
self.log.error("PRC enable failed!")
raise RuntimeError('PRC enable failed!')
return
# Disable PRC:
self.poke32(self.DB_ENABLE_OFFSET, self.DISABLE_PRC)
if check_pll_enabled():
self.log.error('PRC reset failed!')
raise RuntimeError('PRC reset failed!')
def enable_daughterboard(self, db_id, enable=True):
""" Enable or disable clock forwarding to a given DB """
if db_id == 0:
release_reset = self.RELEASE_RST_DB0
assert_reset = self.ASSERT_RST_DB0
else:
release_reset = self.RELEASE_RST_DB1
assert_reset = self.ASSERT_RST_DB1
value = self.peek32(self.DB_ENABLE_OFFSET)
if enable:
# De-assert reset
value = (value | release_reset) & (~assert_reset)
else: #disable
# Assert reset
value = (value | assert_reset) & (~release_reset)
self.poke32(self.DB_ENABLE_OFFSET, value)
def enable_daughterboard_support_clock(self, db_id, enable=True):
""" Enable or disable clock forwarding to a given DB """
if db_id == 0:
clk_enable = self.ENABLE_CLK_DB0
clk_disable = self.DISABLE_CLK_DB0
else:
clk_enable = self.ENABLE_CLK_DB1
clk_disable = self.DISABLE_CLK_DB1
value = self.peek32(self.DB_ENABLE_OFFSET)
if enable:
# Enable clock
value = (value | clk_enable) & (~clk_disable)
else: #disable
# Disable clock
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.
The value has to match as the register offsets depends on them.
Furthermore there needs to be a minimum revision to check for existence
of functionality.
"""
cpld_image_compat_revision = self.peek32(self.OLDEST_COMPAT_REV_OFFSET)
if cpld_image_compat_revision < self.OLDEST_REQ_COMPAT_REV:
error_message = (
'MB CPLD oldest compatible revision'
f' 0x{cpld_image_compat_revision:08x} is out of date. Update'
f' your CPLD image to 0x{self.OLDEST_REQ_COMPAT_REV:08x}.')
self.log.error(error_message)
raise RuntimeError(error_message)
if cpld_image_compat_revision > self.OLDEST_REQ_COMPAT_REV:
error_message = (
'MB CPLD oldest compatible revision'
f' 0x{cpld_image_compat_revision:08x} is unknown. Downgrade'
f' your CPLD image to 0x{self.OLDEST_REQ_COMPAT_REV:08x}.')
self.log.error(error_message)
raise RuntimeError(error_message)
if not self.has_compat_version(self.REQ_COMPAT_REV):
error_message = (
"MB CPLD compatible revision is too old. Update your CPLD"
f" image to at least 0x{self.REQ_COMPAT_REV:08x}.")
self.log.error(error_message)
raise RuntimeError(error_message)
def has_compat_version(self, min_required_version):
"""
Check for a minimum required version.
"""
if min_required_version < self.REQ_COMPAT_REV:
self.log.warning(
"Somebody called MB CPLD has_compat_version with revision"
f" 0x{min_required_version:x} which is older than the mandated"
f" version 0x{self.REQ_COMPAT_REV:x}.")
cpld_image_compat_revision = self.peek32(self.COMPAT_REV_OFFSET)
return cpld_image_compat_revision >= min_required_version
def trace_git_hash(self):
"""
Trace build of MB CPLD
"""
git_hash_rb = self.peek32(self.GIT_HASH_OFFSET)
(git_hash, dirtiness_qualifier) = parse_encoded_git_hash(git_hash_rb)
self.log.trace("MB CPLD build GIT Hash: {:07x} ({})".format(
git_hash, dirtiness_qualifier))
def set_serial_number(self, serial_number):
"""
Set serial number register
"""
assert len(serial_number) > 0
assert len(serial_number) <= 8
serial_number_string = str(serial_number, 'ascii')
serial_number_int = int(serial_number_string, 16)
self.poke32(self.SERIAL_NO_LO_OFFSET, serial_number_int & 0xFFFFFFFF)
self.poke32(self.SERIAL_NO_HI_OFFSET, serial_number_int >> 32)
def set_cmi_device_ready(self, ready=True):
"""
Inform CMI partner that this device is ready for PCI-Express communication.
"""
value = 1 if ready else 0
self.poke32(self.CMI_OFFSET, value)
def get_cmi_status(self):
"""
Return true if upstream CMI device was found.
"""
return bool(self.peek32(self.CMI_OFFSET))