Coverage for apio / scons / gtkwave_util.py: 92%
32 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 02:47 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 02:47 +0000
1# GTKWave related utils.
3"""GTKWave related utilities for the Apio Scons sub process."""
5import re
6import sys
7from pathlib import Path
8from vcdvcd import VCDVCD
9from apio.common.apio_console import cerror
11GTKW_AUTO_FILE_MARKER = "THIS FILE WAS GENERATED AUTOMATICALLY BY APIO"
14def _get_gtkw_file_header(testbench_path: str) -> str:
15 """Return a header string for the auto generated .gtkw file. 'testbench'
16 is the relative path of the testbench file."""
18 # -- Normalized path with '/', even on windows.
19 tb_path_posix = Path(testbench_path).as_posix()
21 lines = [
22 f"# GTKWave display configuration for 'apio sim {tb_path_posix}'",
23 f"# {GTKW_AUTO_FILE_MARKER}.",
24 "# DO NOT EDIT IT MANUALLY!",
25 f"# To customize this file, run 'apio sim {tb_path_posix}'",
26 "# and save the file from GTKWave.",
27 "",
28 "[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI",
29 "",
30 "[*]",
31 ]
33 return "\n".join(lines) + "\n"
36def create_gtkwave_file(
37 testbench_path: str, vcd_path: str, gtkw_path: str
38) -> None:
39 """Generates a GTKWave configuration file from a VCD file.
41 Args:
42 testbench_path (str): Path to the simulated testbench.
43 vcd_path (str): Path to the input VCD file.
44 gtkw_path (str): Path to the output GTKWave configuration file.
45 """
47 # -- Pattern for top levels signals. E.g. 'testbench.CLK'.
48 pattern = re.compile(r"^[^.]+[.][^.]+$")
50 # -- Parse the vcd file and load the signals that match the pattern.
51 # -- Do not load the actual signals values, just the metadata.
52 vcd = VCDVCD(vcd_path, signal_res=[pattern], store_tvs=False)
54 # -- Get a list with raw names of matching signals.
55 signals = list(vcd.references_to_ids.keys())
57 # -- Sort, case insensitive.
58 signals.sort(key=str.casefold)
60 # -- Write the output file.
61 with open(gtkw_path, "w", encoding="utf-8") as f:
62 f.write(_get_gtkw_file_header(testbench_path))
63 for signal in signals:
64 f.write(signal + "\n")
67def is_user_gtkw_file(gtkw_path: str) -> bool:
68 """Test if the given .gtkw file exists and contains user's saved
69 GTKWave display configuration."""
71 # pylint: disable=broad-exception-caught
73 assert gtkw_path.endswith(".gtkw")
75 # -- If doesn't exist than now.
76 if not Path(gtkw_path).exists():
77 return False
79 # -- File exists, test the content.
80 try:
81 # with gtkw_path.open("r", encoding="utf-8", errors="replace") as f:
82 with open(gtkw_path, "r", encoding="utf-8", errors="replace") as f:
83 for line in f:
84 if GTKW_AUTO_FILE_MARKER in line:
85 # -- File contains the apio auto marker. Not a user file.
86 return False
87 # -- Marker not found. Must be a user file.
88 return True
90 except Exception as e:
91 cerror(
92 f"Failed to scan existing .gtkw file {gtkw_path}",
93 f"{type(e).__name__}: {e}",
94 )
95 sys.exit(1)