summaryrefslogtreecommitdiff
path: root/util/ec_openocd.py
diff options
context:
space:
mode:
authorrobertzieba@google.com <robertzieba@google.com>2022-02-25 16:25:07 -0700
committerCommit Bot <commit-bot@chromium.org>2022-03-15 17:00:14 +0000
commitc3dd6d0fc27f0d844dced10e27210efe76b1e344 (patch)
tree9d76b6fd4918941dd350a7ab421c8ab9a4e059e6 /util/ec_openocd.py
parent219b8c53616c23797e6a4fde75d7ac028e2b151e (diff)
downloadchrome-ec-c3dd6d0fc27f0d844dced10e27210efe76b1e344.tar.gz
ec/util: Add support for EC flashing/debugging through OpenOCD
The existing flash_ec script does have support for OpenOCD, however it also requires running servo as well. And it does not support the NPCX99nf line of microcontrollers, nor does it support directly flashing NPCX devices. This change adds a ec_openocd.py script that serves as a wrapper around OpenOCD and gdb to make it easy to perform flashing and debugging through OpenOCD. BUG=b:208517780 BRANCH=none TEST=Verified that flashing and debugging worked as expected Change-Id: I8f4e127ada871f31840b07d20855cb2aa4d9cc54 Signed-off-by: robertzieba@google.com <robertzieba@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3498594 Reviewed-by: Jack Rosenthal <jrosenth@chromium.org>
Diffstat (limited to 'util/ec_openocd.py')
-rwxr-xr-xutil/ec_openocd.py224
1 files changed, 224 insertions, 0 deletions
diff --git a/util/ec_openocd.py b/util/ec_openocd.py
new file mode 100755
index 0000000000..a84c00643c
--- /dev/null
+++ b/util/ec_openocd.py
@@ -0,0 +1,224 @@
+#!/usr/bin/env python3
+
+# Copyright 2022 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import dataclasses
+import pathlib
+import socket
+import subprocess
+import sys
+import time
+
+"""
+Flashes and debugs the EC through openocd
+"""
+
+@dataclasses.dataclass
+class BoardInfo:
+ gdb_variant: str
+ num_breakpoints: int
+ num_watchpoints: int
+
+
+# Debuggers for each board, OpenOCD currently only supports GDB
+boards = {
+ "skyrim": BoardInfo("arm-none-eabi-gdb", 6, 4)
+}
+
+
+def create_openocd_args(interface, board):
+ if not board in boards:
+ raise RuntimeError(f"Unsupported board {board}")
+
+ board_info = boards[board]
+ args = [
+ "openocd",
+ "-f", f"interface/{interface}.cfg",
+ "-c", "add_script_search_dir openocd",
+ "-f", f"board/{board}.cfg",
+ ]
+
+ return args
+
+
+def create_gdb_args(board, port, executable):
+ if not board in boards:
+ raise RuntimeError(f"Unsupported board {board}")
+
+ board_info = boards[board]
+ args = [
+ board_info.gdb_variant,
+ executable,
+ # GDB can't autodetect these according to OpenOCD
+ "-ex", f"set remote hardware-breakpoint-limit {board_info.num_breakpoints}",
+ "-ex", f"set remote hardware-watchpoint-limit {board_info.num_watchpoints}",
+
+ # Connect to OpenOCD
+ "-ex", f"target extended-remote localhost:{port}",
+ ]
+
+ return args
+
+
+def flash(interface, board, image, verify):
+ print(f"Flashing image {image}")
+ # Run OpenOCD and pipe its output to stdout
+ # OpenOCD will shutdown after the flashing is completed
+ args = create_openocd_args(interface, board)
+ args += ["-c", f'init; flash_target "{image}" {int(verify)}; shutdown']
+
+ subprocess.run(args, stdout=sys.stdout, stderr=subprocess.STDOUT)
+
+
+def debug(interface, board, port, executable):
+ # Start OpenOCD in the background
+ openocd_args = create_openocd_args(interface, board)
+ openocd_args += ["-c", f"gdb_port {port}"]
+
+ openocd = subprocess.Popen(
+ openocd_args, encoding="utf-8", stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+ )
+
+ # Wait for OpenOCD to start, it'll open a port for GDB connections
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ connected = False
+ for i in range(0, 10):
+ print("Waiting for OpenOCD to start...")
+ connected = sock.connect_ex(("localhost", port))
+ if connected:
+ break
+
+ time.sleep(1000)
+
+ if not connected:
+ print(f"Failed to connect to OpenOCD on port {port}")
+ return
+
+ sock.close()
+
+ gdb_args = create_gdb_args(board, port, executable)
+ # Start GDB
+ gdb = subprocess.Popen(
+ gdb_args, stdout=sys.stdout, stderr=subprocess.STDOUT, stdin=sys.stdin
+ )
+
+ openocd_out = ""
+ while gdb.poll() == None and openocd.poll() == None:
+ (output, _) = openocd.communicate()
+ openocd_out += output
+
+ # Wait for OpenOCD to shutdown
+ print("Waiting for OpenOCD to finish...")
+ if openocd.poll() == None:
+ try:
+ # Read the last bit of stdout
+ (output, _) = openocd.communicate(timeout=3)
+ openocd_out += output
+ except subprocess.TimeoutExpired:
+ # OpenOCD didn't shutdown, kill it
+ openocd.kill()
+
+ if openocd.returncode != 0:
+ print("OpenOCD failed to shutdown cleanly: ")
+ print(openocd_out)
+
+
+def get_flash_file(board):
+ return (
+ pathlib.Path(__file__).parent
+ / ".."
+ / "build"
+ / "zephyr"
+ / board
+ / "output"
+ / "zephyr.bin"
+ ).resolve()
+
+
+def get_executable_file(board):
+ return (
+ pathlib.Path(__file__).parent
+ / ".."
+ / "build"
+ / "zephyr"
+ / board
+ / "output"
+ / "zephyr.ro.elf"
+ ).resolve()
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--board",
+ "-b",
+ choices=boards.keys(),
+ required=True,
+ )
+
+ parser.add_argument(
+ "--interface",
+ "-i",
+ default="jlink",
+ help="The JTAG interface to use",
+ )
+
+ parser.add_argument(
+ "--file",
+ "-f",
+ type=pathlib.Path,
+ default=None,
+ help="The file to use, see each sub-command for what the file is used for",
+ )
+
+ sub_parsers = parser.add_subparsers(help="sub-command -h for specific help")
+ flash_parser = sub_parsers.add_parser(
+ "flash",
+ help="Flashes an image to the target EC, \
+ FILE selects the image to flash, defaults to the zephyr image",
+ )
+ flash_parser.set_defaults(command="flash")
+ flash_parser.add_argument(
+ "--verify",
+ "-v",
+ default=True,
+ help="Verify flash after writing image, defaults to true",
+ )
+
+ debug_parser = sub_parsers.add_parser(
+ "debug",
+ help="Debugs the target EC through GDB, \
+ FILE selects the executable to load debug info from, defaults to using the zephyr RO executable",
+ )
+ debug_parser.set_defaults(command="debug")
+ debug_parser.add_argument(
+ "--port",
+ "-p",
+ help="The port for GDB to connect to",
+ type=int,
+ default=3333,
+ )
+
+ args = parser.parse_args()
+ # Get the image path if we were given one
+ target_file = None
+ if args.file != None:
+ target_file = args.file.resolve()
+
+ if args.command == "flash":
+ image_file = get_flash_file(args.board) if target_file == None else target_file
+ flash(args.interface, args.board, image_file, args.verify)
+ elif args.command == "debug":
+ executable_file = (
+ get_executable_file(args.board) if target_file == None else target_file
+ )
+ debug(args.interface, args.board, args.port, executable_file)
+ else:
+ parser.print_usage()
+
+
+if __name__ == "__main__":
+ main()