summaryrefslogtreecommitdiff
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
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>
-rwxr-xr-xutil/ec_openocd.py224
-rw-r--r--util/openocd/board/skyrim.cfg11
-rw-r--r--util/openocd/target/npcx993f.cfg11
-rw-r--r--util/openocd/target/npcx99nf.cfg106
4 files changed, 352 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()
diff --git a/util/openocd/board/skyrim.cfg b/util/openocd/board/skyrim.cfg
new file mode 100644
index 0000000000..a8047ade22
--- /dev/null
+++ b/util/openocd/board/skyrim.cfg
@@ -0,0 +1,11 @@
+# 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.
+
+transport select swd
+
+source [find target/npcx993f.cfg]
+
+proc flash_board { image verify } {
+ flash_target $image $verify
+} \ No newline at end of file
diff --git a/util/openocd/target/npcx993f.cfg b/util/openocd/target/npcx993f.cfg
new file mode 100644
index 0000000000..690bee489d
--- /dev/null
+++ b/util/openocd/target/npcx993f.cfg
@@ -0,0 +1,11 @@
+# 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.
+
+# Also works for the 7F and 6F as these all share 64K of RAM at 0x200C0000
+set WORKAREASIZE 0x10000
+set WORKAREAADDR 0x200c0000
+set CHIPNAME NPCX993F
+set CPUTAPID 0x2ba01477
+
+source [find target/npcx99nf.cfg] \ No newline at end of file
diff --git a/util/openocd/target/npcx99nf.cfg b/util/openocd/target/npcx99nf.cfg
new file mode 100644
index 0000000000..14dbac9f13
--- /dev/null
+++ b/util/openocd/target/npcx99nf.cfg
@@ -0,0 +1,106 @@
+# 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.
+
+source [find mem_helper.tcl]
+
+set _ENDIAN little
+
+if { [info exists CHIPNAME] } {
+ set _CHIPNAME $CHIPNAME
+} else {
+ set _CHIPNAME NPCX99nF
+}
+
+if { [info exists WORKAREAADDR] } {
+ set _WORKAREAADDR $WORKAREAADDR
+} else {
+ set _WORKAREAADDR 0
+}
+
+if { [info exists WORKAREASIZE] } {
+ set _WORKAREASIZE $WORKAREASIZE
+} else {
+ set _WORKAREASIZE 0
+}
+
+if { [info exists CPUTAPID ] } {
+ set _CPUTAPID $CPUTAPID
+} else {
+ set _CPUTAPID 0x2ba01477
+}
+
+if { [info exists FLASHADDR] } {
+ set _FLASHADDR $FLASHADDR
+} else {
+ set _FLASHADDR 0x64000000
+}
+
+adapter speed 600
+
+swd newdap $_CHIPNAME cpu -enable
+dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap
+
+# Work area in RAM used during flashing
+$_TARGETNAME configure -work-area-phys $_WORKAREAADDR -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+# The npcx driver detects and autoconfigures various flash parameters
+set _FLASHNAME $_CHIPNAME.internal_flash
+flash bank $_FLASHNAME npcx $_FLASHADDR 0 0 0 $_TARGETNAME
+
+# use sysresetreq to perform a system reset
+cortex_m reset_config sysresetreq
+adapter srst delay 100
+
+# Freeze various clocks, counters and destructive reads
+# Otherwise something like the watchdog time can interfere with debugging/flashing
+proc debug_freeze { } {
+ mwb 0x400C3078 0x7F
+}
+
+proc handle_gdb_attach { } {
+ halt
+ debug_freeze
+
+ # Read the address of the reset handler from the vector table
+ # The bootloader runs first, but it's not a good place to halt
+ # as it sets up various things, including debug support
+ set RESET_HANDLER [mrw 0x10080004]
+ bp $RESET_HANDLER 2 hw
+ reset run
+
+ # Wait to hit the reset handler bp
+ wait_halt 250
+ # OpenOCD can get stuck in the reset handler
+ # removing the BP prevents that
+ rbp $RESET_HANDLER
+}
+
+proc handle_gdb_detach { } {
+ shutdown
+}
+
+proc flash_target { image verify } {
+ global _FLASHADDR
+ echo "Resetting and halting target..."
+ reset halt
+ echo "Flashing image..."
+ flash write_image erase $image $_FLASHADDR bin
+ if { $verify == 1} {
+ echo "Verifying image..."
+ flash verify_image $image $_FLASHADDR bin
+ }
+ echo "Resuming target..."
+ reset run
+}
+
+$_TARGETNAME configure -event halted debug_freeze
+
+# GDB connection config
+$_TARGETNAME configure -event gdb-attach handle_gdb_attach
+$_TARGETNAME configure -event gdb-detach handle_gdb_detach
+
+gdb_memory_map enable \ No newline at end of file