diff options
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/zbx_update_cpld.py | 289 |
1 files changed, 219 insertions, 70 deletions
diff --git a/mpm/python/usrp_mpm/dboard_manager/zbx_update_cpld.py b/mpm/python/usrp_mpm/dboard_manager/zbx_update_cpld.py index 851cfe997..12cd6c0ed 100644 --- a/mpm/python/usrp_mpm/dboard_manager/zbx_update_cpld.py +++ b/mpm/python/usrp_mpm/dboard_manager/zbx_update_cpld.py @@ -13,7 +13,9 @@ import os import argparse import subprocess import pyudev +import re from usrp_mpm.mpmlog import get_logger +from usrp_mpm.mpmlog import get_main_logger from usrp_mpm.mpmutils import check_fpga_state from usrp_mpm.sys_utils.sysfs_gpio import GPIOBank from usrp_mpm.periph_manager.x4xx_periphs import CtrlportRegs @@ -22,13 +24,6 @@ from usrp_mpm.chips.max10_cpld_flash_ctrl import Max10CpldFlashCtrl from usrp_mpm.sys_utils.udev import dt_symbol_get_spidev OPENOCD_DIR = "/usr/share/openocd/scripts" -CONFIGS = { - 'axi_bitq' : { - 'files' : ["fpga/altera-10m50.cfg"], - 'cmd' : ["interface axi_bitq; axi_bitq_config %u %u %u; adapter_khz %u", - "init; svf -tap 10m50.tap %s -progress -quiet;exit"] - } -} AXI_BITQ_ADAPTER_SPEED = 5000 AXI_BITQ_BUS_CLK = 50000000 @@ -82,64 +77,131 @@ def find_axi_bitq_uio(): logger.error("Error while looking for axi_bitq uio nodes: {}".format(ex)) return None -def do_update_cpld(filename, daughterboards, updater_mode): +def do_update_cpld(dboard_update_settings): """ Carry out update process for the CPLD - :param filename: path (on device) to the new CPLD image - :param daughterboards: iterable containing dboard numbers to update - :param updater_mode: the updater method to use- Either flash or legacy (JTAG) + :param dboard_update_settings: list of db and corresponding path (on device) to the new CPLD image + and updater mode to use :return: True on success, False otherwise """ - assert updater_mode in ('flash', 'legacy'), \ - f"Invalid updater method {updater_mode} given" + logger = get_logger('update_cpld') - logger.info("Programming CPLD of dboards {} with image {} using {} mode" - .format(daughterboards, filename, updater_mode)) - if not daughterboards: + if not dboard_update_settings: logger.error("Invalid daughterboard selection.") return False - if not os.path.exists(filename): - logger.error("CPLD image file {} not found".format(filename)) - return False - if not check_fpga_state(logger=logger): logger.error("CPLD lines are routed through fabric, FPGA is not programmed, giving up") return False - if updater_mode == 'legacy': - return jtag_cpld_update(filename, daughterboards, logger) - # updater_mode == flash: - for dboard in daughterboards: - dboard = int(dboard, 10) - logger.info("Updating daughterboard slot {}...".format(dboard)) - # enable required daughterboard clock - cpld_spi_node = dt_symbol_get_spidev('mb_cpld') - cpld_control = MboardCPLD(cpld_spi_node, logger) - cpld_control.enable_daughterboard_support_clock(dboard, enable=True) - # setup flash configuration engine and required register access - label = "ctrlport-mboard-regs" - ctrlport_regs = CtrlportRegs(label, logger) - regs = ctrlport_regs.get_db_cpld_iface(dboard) - flash_control = Max10CpldFlashCtrl( - logger, regs, RECONFIG_ENGINE_OFFSET, CPLD_MIN_REVISION) - success = flash_control.update(filename) - # disable clock - cpld_control.enable_daughterboard_support_clock(dboard, enable=False) + for dboard_update_setting in dboard_update_settings: + dboard = dboard_update_setting[0] + filename = dboard_update_setting[1] + updater_mode = dboard_update_setting[2] + cpld_update_strategies = dboard_update_setting[3] + + logger.info("Programming CPLD of dboard {} with image {} using {} mode" + .format(dboard, filename, updater_mode)) + + if not os.path.exists(filename): + logger.error("CPLD image file {} not found".format(filename)) + return False + + if updater_mode == 'legacy': + success = jtag_cpld_update(filename, dboard, cpld_update_strategies, logger) + elif updater_mode == 'flash': + success = flash_cpld_update(filename, dboard, cpld_update_strategies, logger) + else: + raise NotImplementedError("Unknown updater mode {}".format(updater_mode)) + if not success: return success + return True -def jtag_cpld_update(filename, daughterboards, logger=None): +def get_db_rev(slot): + assert slot in [0,1] + cmd = ['eeprom-dump', 'db{}'.format(slot)] + output = subprocess.check_output( + cmd, + stderr=subprocess.STDOUT, + ).decode('utf-8') + expression = re.compile("^usrp_eeprom_board_info.*rev: 0x([0-9A-Fa-f]+).*compat_rev:.*") + for line in output.splitlines(): + match = expression.match(line) + if match: + rev = int(match.group(1), 16) + return rev + raise AssertionError("Cannot get rev from DB{} eeprom.: `{}'".format(slot, output)) + +def get_cpld_update_strategies(rev): + """Determine the CPLD update strategies based on the rev""" + + cpld_image_10m04_update_strategies = {'cpld_model': '10m04', + 'updaters': ['flash', 'legacy'], + 'default_updater': 'flash', + 'image_names': {'flash': ['cpld-zbx-10m04.rpd', 'usrp_zbx_cpld_10m04.rpd'], + 'legacy': ['cpld-zbx-10m04.svf', 'usrp_zbx_cpld_10m04.svf']}, + 'updater_config': {'legacy': {'files' : ["fpga/altera-10m50.cfg"], + 'cmd' : ["interface axi_bitq; axi_bitq_config %u %u %u; adapter_khz %u", + "init; svf -tap 10m50.tap %s -progress -quiet;exit"]}}} + + cpld_image_xo3lf_update_strategies = {'cpld_model': 'xo3lf', + 'updaters': ['legacy'], + 'default_updater': 'legacy', + 'image_names': {'legacy': ['cpld-zbx-xo3lf.svf', 'usrp_zbx_cpld_xo3lf.svf']}, + 'updater_config': {'legacy': {'files' : ["fpga/lattice-xo3lf.cfg"], + 'cmd' : ["interface axi_bitq; axi_bitq_config %u %u %u; adapter_khz %u", + "init; svf -tap xo3lf.tap %s -progress -quiet;exit"]}}} + cpld_update_strategies = { + 1: cpld_image_10m04_update_strategies, # revA + 2: cpld_image_10m04_update_strategies, # revB + 3: cpld_image_10m04_update_strategies, # revC + 4: cpld_image_xo3lf_update_strategies, # revD + '10m04': cpld_image_10m04_update_strategies, # 10m04 + 'xo3lf': cpld_image_xo3lf_update_strategies # xo3lf + } + + if rev not in cpld_update_strategies: + raise NotImplementedError("The CPLD update strategy for rev or CPLD model {} is not available".format(rev)) + return cpld_update_strategies[rev] + +def flash_cpld_update(filename, dboard, cpld_update_strategies=None, logger=None): + """ + Carry out update process for the CPLD using flash mode + :param filename: path (on device) to the new CPLD image + :param dboard: dboard to update + :return: True on success, False otherwise + """ + dboard = int(dboard, 10) + logger.info("Updating daughterboard slot {}...".format(dboard)) + # enable required daughterboard clock + cpld_spi_node = dt_symbol_get_spidev('mb_cpld') + cpld_control = MboardCPLD(cpld_spi_node, logger) + cpld_control.enable_daughterboard_support_clock(dboard, enable=True) + # setup flash configuration engine and required register access + label = "ctrlport-mboard-regs" + ctrlport_regs = CtrlportRegs(label, logger) + regs = ctrlport_regs.get_db_cpld_iface(dboard) + flash_control = Max10CpldFlashCtrl( + logger, regs, RECONFIG_ENGINE_OFFSET, CPLD_MIN_REVISION) + success = flash_control.update(filename) + # disable clock + cpld_control.enable_daughterboard_support_clock(dboard, enable=False) + if success: + logger.trace("Done programming CPLD...") + return success + +def jtag_cpld_update(filename, dboard, cpld_update_strategies, logger=None): """ Carry out update process for the CPLD :param filename: path (on device) to the new CPLD image - :param daughterboards: iterable containing dboard numbers to update + :param dboard: dboard to update + :param cpld_update_strategies: a data struct containing updaters, image names, metadata. :return: True on success, False otherwise """ - mode = 'axi_bitq' - config = CONFIGS[mode] + config = cpld_update_strategies['updater_config']['legacy'] if check_openocd_files(config['files'], logger=logger): logger.trace("Found required OpenOCD files.") @@ -147,24 +209,23 @@ def jtag_cpld_update(filename, daughterboards, logger=None): # check_openocd_files logs errors return False - for dboard in daughterboards: - logger.info("Updating daughterboard slot {}...".format(dboard)) + logger.info("Updating daughterboard slot {}...".format(dboard)) - uio_id = find_axi_bitq_uio() - offset = find_offset(int(dboard, 10)) - if uio_id is None or uio_id < 0: - logger.error('Failed to find axi_bitq uio devices. '\ - 'Make sure overlays are up to date') - return False + uio_id = find_axi_bitq_uio() + offset = find_offset(int(dboard, 10)) + if uio_id is None or uio_id < 0: + logger.error('Failed to find axi_bitq uio devices. '\ + 'Make sure overlays are up to date') + return False - cmd = [ - "openocd", - "-c", config['cmd'][0] % (uio_id, AXI_BITQ_BUS_CLK, offset, AXI_BITQ_ADAPTER_SPEED), - "-f", (config['files'][0]).strip(), - "-c", config['cmd'][1] % filename] + cmd = [ + "openocd", + "-c", config['cmd'][0] % (uio_id, AXI_BITQ_BUS_CLK, offset, AXI_BITQ_ADAPTER_SPEED), + "-f", (config['files'][0]).strip(), + "-c", config['cmd'][1] % filename] - logger.trace("Update CPLD CMD: {}".format(" ".join(cmd))) - subprocess.call(cmd) + logger.trace("Update CPLD CMD: {}".format(" ".join(cmd))) + subprocess.call(cmd) logger.trace("Done programming CPLD...") return True @@ -177,13 +238,14 @@ def main(): def parse_args(): """Parse the command-line arguments""" parser = argparse.ArgumentParser(description='Update the CPLD image on ZBX daughterboard') - parser.add_argument("--file", help="Filename of CPLD image", - default="/lib/firmware/ni/cpld-zbx.rpd") + parser.add_argument("--file", help="Filename of CPLD image. Also specify the updater using" + " --updater arg when using this argument.", + default="") parser.add_argument("--dboards", help="Slot name to program", default="0,1") parser.add_argument("--updater", help="The image updater method to use, either " " 'legacy' (uses openocd) or 'flash'", - default="flash") + default="") parser.add_argument( '-v', '--verbose', @@ -198,21 +260,108 @@ def main(): action="count", default=0 ) - return parser.parse_args() + parser.add_argument("--force", help="Force installing the CPLD image specified by the --file " \ + "argument if it does not match the name of the default CPLD image. " \ + "Using the wrong CPLD image may brick your device.", + action="store_true", default=False, required=False) + parser.add_argument("--cpld_type", + help="Specify the CPLD type. Currently supported types are '10m04' or 'xo3lf'." + " Use this argument to explicitly specify the CPLD hardware type and" + " skip internal revision compatibilty checks. Use this only if you" + " fully understand the hardware being used." + " e.g. zbx_update_cpld --cpld_type '10m04' ", + default=None) + + args = parser.parse_args() + + dboards = args.dboards.split(",") + if any([x not in ('0', '1') for x in dboards]): + log.error("Unsupported dboards requested: %s", dboards) + return False + + if (args.cpld_type): + # Hardware Specified. Skip all checks. + return args + + if (args.file and not args.updater): + parser.epilog = "\nERROR: When setting --file, please also specify the updater to use " \ + "using the --updater argument." + parser.print_help() + parser.epilog = None + sys.exit(1) + + for dboard in dboards: + dboard_rev = get_db_rev(int(dboard, 10)) + cpld_update_strategies = get_cpld_update_strategies(dboard_rev) + + if args.updater and args.updater not in cpld_update_strategies['updaters']: + parser.epilog = "\nERROR: Valid updaters for zbx rev {} are {}, " \ + "but you selected {}.".format( + dboard_rev, + ' and '.join(cpld_update_strategies['updaters']), + args.updater + ) + parser.print_help() + parser.epilog = None + sys.exit(1) + + default_image_names = [] + for updater in cpld_update_strategies['updaters']: + default_image_names += cpld_update_strategies['image_names'][updater] + if args.file and (os.path.basename(args.file) not in default_image_names) and not args.force: + parser.epilog = "\nERROR: Valid CPLD image names for zbx rev {} are {}, " \ + "but you selected {}. Using the wrong CPLD image may brick your device. " \ + "Please use the --force option if you are really sure.".format( + dboard_rev, + ' and '.join(default_image_names), + args.file + ) + parser.print_help() + parser.epilog = None + sys.exit(1) + + if args.file and args.updater and (os.path.basename(args.file) not in cpld_update_strategies['image_names'][args.updater]) and not args.force: + parser.epilog = "\nERROR: Invalid CPLD image name and updater mode combination for zbx rev {}" \ + "Using the wrong CPLD image may brick your device. " \ + "Please use the --force option if you are really sure.".format( + dboard_rev, + ) + parser.print_help() + parser.epilog = None + sys.exit(1) + + return args + + def get_dboard_update_settings(args, dboards): + """Determine the dboard cpld image and updater mappings""" + dboard_update_settings = [] + for dboard in dboards: + dboard_rev = get_db_rev(int(dboard, 10)) + if args.cpld_type: + cpld_update_strategies = get_cpld_update_strategies(args.cpld_type) + else: + cpld_update_strategies = get_cpld_update_strategies(dboard_rev) + + if args.updater: + updater = args.updater + else: + updater = cpld_update_strategies['default_updater'] + + if args.file: + image_path = args.file + else: + image_names = cpld_update_strategies['image_names'][updater] + image_path = "/lib/firmware/ni/" + image_names[0] + + dboard_update_settings.append([dboard, image_path, updater, cpld_update_strategies]) + return dboard_update_settings args = parse_args() - # We need to make a logger if we're running stand-alone - from usrp_mpm.mpmlog import get_main_logger log = get_main_logger(log_default_delta=args.verbose-args.quiet) dboards = args.dboards.split(",") - if any([x not in ('0', '1') for x in dboards]): - log.error("Unsupported dboards requested: %s", dboards) - return False - - return do_update_cpld(args.file, dboards, args.updater) - + return do_update_cpld(get_dboard_update_settings(args, dboards)) if __name__ == "__main__": sys.exit(not main()) |