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

1# GTKWave related utils. 

2 

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

4 

5import re 

6from pathlib import Path 

7from vcdvcd import VCDVCD 

8from apio.common.apio_console import fatal_error 

9 

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

11 

12 

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

16 

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

18 tb_path_posix = Path(testbench_path).as_posix() 

19 

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 ] 

31 

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

33 

34 

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. 

39 

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

45 

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

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

48 

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) 

52 

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

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

55 

56 # -- Sort, case insensitive. 

57 signals.sort(key=str.casefold) 

58 

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

64 

65 

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

69 

70 # pylint: disable=broad-exception-caught 

71 

72 assert gtkw_path.endswith(".gtkw") 

73 

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

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

76 return False 

77 

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 

88 

89 except Exception as e: 

90 fatal_error(f"Failed to scan existing .gtkw file {gtkw_path}", cause=e)