From 6891141fa6e19b030c75ae200c51eacb39612f09 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 15 Jan 2018 11:43:29 +0000 Subject: [PATCH] artiq_flash: add sayma support. --- DEVELOPER_NOTES.rst | 7 +- artiq/frontend/artiq_devtool.py | 146 ++++++++++++++++++---------- artiq/gateware/targets/sayma_rtm.py | 24 ++++- 3 files changed, 120 insertions(+), 57 deletions(-) diff --git a/DEVELOPER_NOTES.rst b/DEVELOPER_NOTES.rst index 460a16d05..3c6724f9a 100644 --- a/DEVELOPER_NOTES.rst +++ b/DEVELOPER_NOTES.rst @@ -34,7 +34,8 @@ To avoid conflicts for development boards on the server, while using a board you To lock the KC705 for 30 minutes or until Ctrl-C is pressed: :: - flock --verbose /run/boards/kc705 sleep 1800 + flock --verbose /run/boards/kc705_1 sleep 1800 + Check that the command acquires the lock, i.e. prints something such as: :: flock: getting lock took 0.000003 seconds @@ -42,7 +43,7 @@ Check that the command acquires the lock, i.e. prints something such as: To lock the KC705 for the duration of the execution of a shell: :: - flock /run/boards/kc705 bash + flock /run/boards/kc705_1 bash You may also use this script: :: @@ -53,7 +54,7 @@ If the board is already locked by another user, the ``flock`` commands above wil To determine which user is locking a board, use: :: - fuser -v /run/boards/kc705 + fuser -v /run/boards/kc705_1 Selecting a development board with artiq_flash diff --git a/artiq/frontend/artiq_devtool.py b/artiq/frontend/artiq_devtool.py index fa3236fd7..021b51f9c 100755 --- a/artiq/frontend/artiq_devtool.py +++ b/artiq/frontend/artiq_devtool.py @@ -22,30 +22,33 @@ def get_argparser(): verbosity_args(parser) - parser.add_argument("-H", "--host", metavar="HOSTNAME", - type=str, default="lab.m-labs.hk", - help="SSH host where the development board is located") - parser.add_argument("-D", "--device", metavar="HOSTNAME", - type=str, default="kc705.lab.m-labs.hk", - help="address or domain corresponding to the development board") - parser.add_argument("-s", "--serial", metavar="PATH", - type=str, default="/dev/ttyUSB_kc705", - help="TTY device corresponding to the development board") - parser.add_argument("-l", "--lockfile", metavar="PATH", - type=str, default="/run/boards/kc705", - help="The lockfile to be acquired for the duration of the actions") - parser.add_argument("-w", "--wait", action="store_true", - help="Wait for the board to unlock instead of aborting the actions") parser.add_argument("-t", "--target", metavar="TARGET", type=str, default="kc705_dds", help="Target to build, one of: " - "kc705_dds kasli sayma_amc_standalone " + "kc705_dds kasli sayma_rtm sayma_amc_standalone " "sayma_amc_drtio_master sayma_amc_drtio_satellite") + parser.add_argument("-H", "--host", metavar="HOSTNAME", + type=str, default="lab.m-labs.hk", + help="SSH host where the development board is located") + parser.add_argument('-b', "--board", metavar="BOARD", + type=str, default=None, + help="Board to connect to on the development SSH host") + parser.add_argument("-d", "--device", metavar="DEVICENAME", + type=str, default="{board}.{hostname}", + help="Address or domain corresponding to the development board") + parser.add_argument("-s", "--serial", metavar="SERIAL", + type=str, default="/dev/ttyUSB_{board}", + help="TTY device corresponding to the development board") + parser.add_argument("-l", "--lockfile", metavar="LOCKFILE", + type=str, default="/run/boards/{board}", + help="The lockfile to be acquired for the duration of the actions") + parser.add_argument("-w", "--wait", action="store_true", + help="Wait for the board to unlock instead of aborting the actions") parser.add_argument("actions", metavar="ACTION", type=str, default=[], nargs="+", help="actions to perform, sequence of: " - "build boot boot+log connect reset hotswap clean") + "build clean reset flash flash+log hotswap") return parser @@ -56,19 +59,47 @@ def main(): if args.verbose == args.quiet == 0: logging.getLogger().setLevel(logging.INFO) - if args.target in ["kc705_dds", "kasli", "sayma_amc_standalone", "sayma_amc_drtio_master"]: - firmware = "runtime" + build_args = [] + if args.target == "kc705_dds": + boardtype, firmware = "kc705", "runtime" + elif args.target == "sayma_amc_standalone": + boardtype, firmware = "sayma", "runtime" + build_args += ["--rtm-csr-csv", "/tmp/sayma_rtm/sayma_rtm_csr.csv"] + elif args.target == "sayma_amc_drtio_master": + boardtype, firmware = "sayma", "runtime" elif args.target == "sayma_amc_drtio_satellite": - firmware = "satman" + boardtype, firmware = "sayma", "satman" + elif args.target == "sayma_rtm": + boardtype, firmware = "sayma_rtm", None else: raise NotImplementedError("unknown target {}".format(args.target)) + flash_args = ["-t", boardtype] + if boardtype == "sayma": + if args.board is None: + args.board = "sayma_1" + if args.board == "sayma_1": + flash_args += ["--preinit-command", "ftdi_location 5:2"] + elif args.board == "sayma_2": + flash_args += ["--preinit-command", "ftdi_location 3:10"] + elif args.board == "sayma_3": + flash_args += ["--preinit-command", "ftdi_location 5:1"] + else: + raise NotImplementedError("unknown --preinit-command for {}".format(boardtype)) + client = SSHClient(args.host) substs = { - "env": "bash -c 'export PATH=$HOME/miniconda/bin:$PATH; exec $0 $*' ", - "serial": args.serial, - "firmware": firmware, + "target": args.target, + "hostname": args.host, + "boardtype": boardtype, + "board": args.board if args.board else boardtype + "_1", + "firmware": firmware, } + substs.update({ + "devicename": args.device.format(**substs), + "lockfile": args.lockfile.format(**substs), + "serial": args.serial.format(**substs), + }) flock_acquired = False flock_file = None # GC root @@ -80,7 +111,7 @@ def main(): logger.info("Acquiring device lock") flock = client.spawn_command("flock --verbose {block} {lockfile} sleep 86400" .format(block="" if args.wait else "--nonblock", - lockfile=args.lockfile), + **substs), get_pty=True) flock_file = flock.makefile('r') while not flock_acquired: @@ -94,44 +125,65 @@ def main(): logger.error("Failed to get lock") sys.exit(1) + def artiq_flash(args, synchronous=True): + args = flash_args + args + args = ["'{}'".format(arg) if " " in arg else arg for arg in args] + cmd = client.spawn_command( + "artiq_flash " + " ".join(args), + **substs) + if synchronous: + client.drain(cmd) + else: + return cmd + for action in args.actions: if action == "build": - logger.info("Building firmware") + logger.info("Building target") try: subprocess.check_call(["python3", "-m", "artiq.gateware.targets." + args.target, "--no-compile-gateware", + *build_args, "--output-dir", - "/tmp/{target}".format(target=args.target)]) + "/tmp/{target}".format(**substs)]) except subprocess.CalledProcessError: logger.error("Build failed") sys.exit(1) elif action == "clean": logger.info("Cleaning build directory") - target_dir = "/tmp/{target}".format(target=args.target) + target_dir = "/tmp/{target}".format(**substs) if os.path.isdir(target_dir): shutil.rmtree(target_dir) - elif action == "boot" or action == "boot+log": - lock() + elif action == "reset": + logger.info("Resetting device") + artiq_flash(["reset"]) - logger.info("Uploading firmware") - client.get_sftp().put("/tmp/{target}/software/{firmware}/{firmware}.bin" - .format(target=args.target, firmware=firmware), - "{tmp}/{firmware}.bin" - .format(tmp=client.tmp, firmware=firmware)) + elif action == "flash" or action == "flash+log": + def upload_product(product, ext): + logger.info("Uploading {}".format(product)) + client.get_sftp().put("/tmp/{target}/software/{product}/{product}.{ext}" + .format(target=args.target, product=product, ext=ext), + "{tmp}/{product}.{ext}" + .format(tmp=client.tmp, product=product, ext=ext)) - logger.info("Booting firmware") - flterm = client.spawn_command( - "{env} python3 flterm.py {serial} " + - "--kernel {tmp}/{firmware}.bin " + - ("--upload-only" if action == "boot" else "--output-only"), - **substs) - artiq_flash = client.spawn_command( - "{env} artiq_flash start", - **substs) - client.drain(flterm) + upload_product("bootloader", "bin") + upload_product(firmware, "fbi") + + logger.info("Flashing firmware") + artiq_flash(["-d", "{tmp}", "proxy", "bootloader", "firmware", + "start" if action == "flash" else ""]) + + if action == "flash+log": + logger.info("Booting firmware") + flterm = client.spawn_command( + "flterm {serial} " + + "--kernel {tmp}/{firmware}.bin " + + ("--upload-only" if action == "boot" else "--output-only"), + **substs) + artiq_flash(["start"], synchronous=False) + client.drain(flterm) elif action == "connect": lock() @@ -187,13 +239,7 @@ def main(): logger.info("Forwarding ports {} to core device and logs from core device" .format(", ".join(map(str, ports)))) client.run_command( - "{env} python3 flterm.py {serial} --output-only", - **substs) - - elif action == "reset": - logger.info("Resetting device") - client.run_command( - "{env} artiq_flash start", + "flterm {serial} --output-only", **substs) elif action == "hotswap": diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index 0632592b1..c438d469d 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import os +import argparse from migen import * from migen.genlib.resetsync import AsyncResetSynchronizer @@ -182,13 +183,28 @@ class SaymaRTM(Module): def main(): - build_dir = "artiq_sayma_rtm" + parser = argparse.ArgumentParser( + description="ARTIQ device binary builder for Kasli systems") + parser.add_argument("--output-dir", default="artiq_sayma_rtm", + help="output directory for generated " + "source files and binaries") + parser.add_argument("--no-compile-gateware", action="store_true", + help="do not compile the gateware, only generate " + "the CSR map") + parser.add_argument("--csr-csv", default=None, + help="store CSR map in CSV format into the " + "specified file") + args = parser.parse_args() + platform = sayma_rtm.Platform() top = SaymaRTM(platform) - os.makedirs(build_dir, exist_ok=True) - with open(os.path.join(build_dir, "sayma_rtm_csr.csv"), "w") as f: + + os.makedirs(args.output_dir, exist_ok=True) + with open(os.path.join(args.output_dir, "sayma_rtm_csr.csv"), "w") as f: f.write(get_csr_csv(top.csr_regions)) - platform.build(top, build_dir=build_dir) + + if not args.no_compile_gateware: + platform.build(top, build_dir=args.output_dir) if __name__ == "__main__":