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