uhd/mpm/python/usrp_mpm/dboard_manager/rhodium_update_cpld.py
2018-11-07 09:39:18 -08:00

165 lines
5.2 KiB
Python
Executable file

#!/usr/bin/env python3
#
# Copyright 2018 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
Update the CPLD image for the N310
"""
import os
import argparse
import subprocess
import pyudev
from usrp_mpm.mpmlog import get_logger
from usrp_mpm.periph_manager.n3xx import MboardRegsControl
OPENOCD_DIR = "/usr/share/openocd/scripts"
CONFIGS = {
'axi_bitq' : {
'files' : ["fpga/altera-10m50.cfg"],
'cmd' : ["interface axi_bitq; axi_bitq_config %u %u; adapter_khz %u",
"init; svf -tap 10m50.tap %s -progress -quiet;exit"]
}
}
AXI_BITQ_ADAPTER_SPEED = 5000
AXI_BITQ_BUS_CLK = 40000000
def check_openocd_files(files, logger=None):
"""
Check if all file required by OpenOCD exist
:param logger: logger object
"""
for ocd_file in files:
if not os.path.exists(os.path.join(OPENOCD_DIR, ocd_file)):
if logger is not None:
logger.error("Missing file %s" % os.path.join(OPENOCD_DIR, ocd_file))
return False
return True
def check_fpga_state(which=0):
"""
Check if the FPGA is operational
:param which: the FPGA to check
"""
logger = get_logger('update_cpld')
try:
context = pyudev.Context()
fpga_mgrs = [dev for dev in context.list_devices(subsystem="fpga_manager")]
if fpga_mgrs:
state = fpga_mgrs[which].attributes.asstring('state')
logger.trace("FPGA State: {}".format(state))
return state == "operating"
except OSError as ex:
logger.error("Error while checking FPGA status: {}".format(ex))
return False
def find_axi_bitq_uio(dboard):
"""
Find the AXI Bitq UIO device
:param dboard: the dboard, can be either 0 or 1
"""
assert dboard < 2 and dboard >= 0
label = 'dboard-jtag-%u' % dboard
logger = get_logger('update_cpld')
try:
context = pyudev.Context()
uios = [dev for dev in context.list_devices(subsystem="uio")]
for uio in uios:
uio_label = uio.attributes.asstring('maps/map0/name')
logger.trace("UIO label: {}, match: {} number: {}".format(
uio_label, uio_label == label, uio.sys_number))
if uio_label == label:
return int(uio.sys_number)
except OSError as ex:
logger.error("Error while looking for axi_bitq uio nodes: {}".format(ex))
return -1
def do_update_cpld(filename, daughterboards):
"""
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
:return: True on success, False otherwise
"""
logger = get_logger('update_cpld')
logger.info("Programming CPLD of dboards {} with image {}".format(daughterboards, filename))
if not daughterboards:
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.error("CPLD lines are routed through fabric, FPGA is not programmed, giving up")
return False
regs = MboardRegsControl('mboard-regs', logger)
compat_maj = regs.get_compat_number()[0]
mode = 'axi_bitq' if compat_maj > 3 else 'sysfsgpio'
logger.info("FPGA has compatibilty number {} using {}".format(compat_maj, mode))
config = CONFIGS[mode]
if check_openocd_files(config['files'], logger=logger):
logger.trace("Found required OpenOCD files.")
else:
# check_openocd_files logs errors
return False
for dboard in daughterboards:
logger.info("Updating daughterboard slot {}...".format(dboard))
uio_id = find_axi_bitq_uio(int(dboard, 10))
if uio_id < 0:
logger.error("Failed to find axi_bitq uio devices, \
make sure overlays are up to date")
continue
cmd = ["openocd",
"-c", config['cmd'][0] % (uio_id, AXI_BITQ_BUS_CLK, 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("Done programming CPLD...")
return True
def main():
"""
Go, go, go!
"""
# We need to make a logger if we're running stand-alone
from usrp_mpm.mpmlog import get_main_logger
log = get_main_logger()
# Do some setup
def parse_args():
"""Parse the command-line arguments"""
parser = argparse.ArgumentParser(description='Update the CPLD image on NI Rhodium')
parser.add_argument("--file", help="Filename of CPLD image",
default="/lib/firmware/ni/cpld-rhodium-revb.svf")
parser.add_argument("--dboards", help="Slot name to program", default="0,1")
return parser.parse_args()
args = 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
return do_update_cpld(args.file, dboards)
if __name__ == "__main__":
exit(not main())