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

1# GTKWave related utils. 

2 

3"""GTKWave related utilities for the Apio Scons sub process.""" 

4 

5import re 

6import sys 

7from pathlib import Path 

8from vcdvcd import VCDVCD 

9from apio.common.apio_console import cerror 

10 

11GTKW_AUTO_FILE_MARKER = "THIS FILE WAS GENERATED AUTOMATICALLY BY APIO" 

12 

13 

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.""" 

17 

18 # -- Normalized path with '/', even on windows. 

19 tb_path_posix = Path(testbench_path).as_posix() 

20 

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 ] 

32 

33 return "\n".join(lines) + "\n" 

34 

35 

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. 

40 

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 """ 

46 

47 # -- Pattern for top levels signals. E.g. 'testbench.CLK'. 

48 pattern = re.compile(r"^[^.]+[.][^.]+$") 

49 

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) 

53 

54 # -- Get a list with raw names of matching signals. 

55 signals = list(vcd.references_to_ids.keys()) 

56 

57 # -- Sort, case insensitive. 

58 signals.sort(key=str.casefold) 

59 

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") 

65 

66 

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.""" 

70 

71 # pylint: disable=broad-exception-caught 

72 

73 assert gtkw_path.endswith(".gtkw") 

74 

75 # -- If doesn't exist than now. 

76 if not Path(gtkw_path).exists(): 

77 return False 

78 

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 

89 

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)