diff options
author | Lars Amsel <lars.amsel@ni.com> | 2021-06-04 08:27:50 +0200 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-06-10 12:01:53 -0500 |
commit | 2a575bf9b5a4942f60e979161764b9e942699e1e (patch) | |
tree | 2f0535625c30025559ebd7494a4b9e7122550a73 /mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py | |
parent | e17916220cc955fa219ae37f607626ba88c4afe3 (diff) | |
download | uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.tar.gz uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.tar.bz2 uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.zip |
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>
Diffstat (limited to 'mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py')
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py b/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py new file mode 100644 index 000000000..927db3360 --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/x4xx_mb_cpld.py @@ -0,0 +1,204 @@ +# +# 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)) |