mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
- Implemented module to handle debug reset requests, ensuring proper synchronization with the AHB-Lite bridge. - Created to integrate JTAG-DTM and Debug Module, facilitating abstract command exposure. - Updated and modules to include JTAG interface signals. - Enhanced the development environment with scripts for running debug sessions using OpenOCD and GDB. - Added tests for management debug reset functionality and JTAG integration, ensuring correct behavior under various scenarios. - Updated SDC generation to include JTAG clock domain and port requirements. - Modified various scripts to support new debug features and ensure compatibility with the Hazard3 architecture.
197 lines
6.6 KiB
Python
197 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Run the local Verilator, OpenOCD, and GDB Hazard3 debug acceptance flow."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import socket
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
|
|
|
|
|
SUCCESS_MARKER = "DEBUG_GDB_PASS"
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--emulator", required=True, type=Path)
|
|
parser.add_argument("--image", required=True, type=Path)
|
|
parser.add_argument("--elf", required=True, type=Path)
|
|
parser.add_argument("--openocd", default="openocd")
|
|
parser.add_argument("--gdb", default="riscv32-unknown-elf-gdb")
|
|
parser.add_argument("--openocd-config", required=True, type=Path)
|
|
parser.add_argument("--log-dir", required=True, type=Path)
|
|
parser.add_argument("--result", required=True, type=Path)
|
|
parser.add_argument("--timeout", type=float, default=120.0)
|
|
parser.add_argument("--sim-time", type=int, default=180)
|
|
return parser.parse_args()
|
|
|
|
|
|
def reserve_port() -> int:
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
|
|
listener.bind(("127.0.0.1", 0))
|
|
return int(listener.getsockname()[1])
|
|
|
|
|
|
def wait_for_port(port: int, timeout: float) -> None:
|
|
deadline = time.monotonic() + timeout
|
|
while time.monotonic() < deadline:
|
|
try:
|
|
with socket.create_connection(("127.0.0.1", port), timeout=0.2):
|
|
return
|
|
except OSError:
|
|
time.sleep(0.1)
|
|
raise TimeoutError(f"timed out waiting for 127.0.0.1:{port}")
|
|
|
|
|
|
def stop(process: subprocess.Popen[object] | None) -> None:
|
|
if process is None or process.poll() is not None:
|
|
return
|
|
process.terminate()
|
|
try:
|
|
process.wait(timeout=5)
|
|
except subprocess.TimeoutExpired:
|
|
process.kill()
|
|
process.wait()
|
|
|
|
|
|
def gdb_commands(elf: Path, gdb_port: int) -> str:
|
|
return "\n".join(
|
|
(
|
|
"set confirm off",
|
|
"set pagination off",
|
|
"set remotetimeout 30",
|
|
"set architecture riscv:rv32",
|
|
f"file {elf}",
|
|
f"target extended-remote 127.0.0.1:{gdb_port}",
|
|
"p/x $pc",
|
|
"set {unsigned int}0x3001ff00 = 0x5a5aa5a5",
|
|
"x/w 0x3001ff00",
|
|
"load",
|
|
"break *0x30000008",
|
|
"continue",
|
|
"p/x $pc",
|
|
"stepi",
|
|
"p/x $pc",
|
|
f'printf "{SUCCESS_MARKER}\\n"',
|
|
"detach",
|
|
"quit",
|
|
"",
|
|
)
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
start = time.monotonic()
|
|
started_at = datetime.now(timezone.utc)
|
|
log_dir = args.log_dir.resolve()
|
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
args.result.parent.mkdir(parents=True, exist_ok=True)
|
|
emulator_log = log_dir / "emulator.log"
|
|
openocd_log = log_dir / "openocd.log"
|
|
gdb_log = log_dir / "gdb.log"
|
|
gdb_script = log_dir / "debug.gdb"
|
|
jtag_port = reserve_port()
|
|
gdb_port = reserve_port()
|
|
emulator: subprocess.Popen[object] | None = None
|
|
openocd: subprocess.Popen[object] | None = None
|
|
error: str | None = None
|
|
status = "failed"
|
|
|
|
try:
|
|
gdb_script.write_text(gdb_commands(args.elf.resolve(), gdb_port), encoding="utf-8")
|
|
with emulator_log.open("w", encoding="utf-8") as emulator_stream:
|
|
emulator = subprocess.Popen(
|
|
[
|
|
str(args.emulator.resolve()),
|
|
"--image",
|
|
str(args.image.resolve()),
|
|
"--sim-time",
|
|
str(args.sim_time),
|
|
"--jtag-port",
|
|
str(jtag_port),
|
|
],
|
|
stdout=emulator_stream,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
)
|
|
wait_for_port(jtag_port, args.timeout)
|
|
|
|
with openocd_log.open("w", encoding="utf-8") as openocd_stream:
|
|
openocd = subprocess.Popen(
|
|
[
|
|
args.openocd,
|
|
"-c",
|
|
f"set jtag_port {jtag_port}",
|
|
"-f",
|
|
str(args.openocd_config.resolve()),
|
|
"-c",
|
|
f"gdb_port {gdb_port}",
|
|
"-c",
|
|
"tcl_port disabled",
|
|
"-c",
|
|
"telnet_port disabled",
|
|
],
|
|
stdout=openocd_stream,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
)
|
|
wait_for_port(gdb_port, args.timeout)
|
|
with gdb_log.open("w", encoding="utf-8") as gdb_stream:
|
|
result = subprocess.run(
|
|
[args.gdb, "--batch", "--nx", "--quiet", "--command", str(gdb_script)],
|
|
stdout=gdb_stream,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
timeout=args.timeout,
|
|
check=False,
|
|
)
|
|
if result.returncode != 0:
|
|
raise RuntimeError(f"GDB exited with status {result.returncode}")
|
|
if SUCCESS_MARKER not in gdb_log.read_text(encoding="utf-8"):
|
|
raise RuntimeError("GDB session did not print the success marker")
|
|
status = "passed"
|
|
except (OSError, RuntimeError, TimeoutError, subprocess.SubprocessError) as exception:
|
|
error = str(exception)
|
|
finally:
|
|
stop(openocd)
|
|
stop(emulator)
|
|
|
|
report = {
|
|
"schema_version": 1,
|
|
"tool": "verilator-openocd-gdb",
|
|
"started_at": started_at.isoformat(),
|
|
"finished_at": datetime.now(timezone.utc).isoformat(),
|
|
"duration_seconds": round(time.monotonic() - start, 3),
|
|
"status": status,
|
|
"success_marker": SUCCESS_MARKER,
|
|
"success_marker_seen": status == "passed",
|
|
"logs": {
|
|
"emulator": str(emulator_log),
|
|
"openocd": str(openocd_log),
|
|
"gdb": str(gdb_log),
|
|
"gdb_script": str(gdb_script),
|
|
},
|
|
}
|
|
if error is not None:
|
|
report["error"] = error
|
|
atomic_write(args.result, json.dumps(report, indent=2, sort_keys=True) + "\n")
|
|
if error is not None:
|
|
print(f"debug session failed: {error}")
|
|
return 1
|
|
print(SUCCESS_MARKER)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|