Coverage for tsfpga/vivado/common.py: 70%

50 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-02-21 20:51 +0000

1# -------------------------------------------------------------------------------------------------- 

2# Copyright (c) Lukas Vik. All rights reserved. 

3# 

4# This file is part of the tsfpga project, a project platform for modern FPGA development. 

5# https://tsfpga.com 

6# https://github.com/tsfpga/tsfpga 

7# -------------------------------------------------------------------------------------------------- 

8 

9from __future__ import annotations 

10 

11from pathlib import Path 

12from shutil import which 

13 

14from vunit.ostools import Process 

15 

16from tsfpga.git_utils import get_git_sha 

17from tsfpga.math_utils import to_binary_string 

18 

19 

20def run_vivado_tcl(vivado_path: Path | None, tcl_file: Path, no_log_file: bool = False) -> bool: 

21 """ 

22 Setting cwd ensures that any .log or .jou files produced are placed in 

23 the same directory as the TCL file that produced them. 

24 

25 Arguments: 

26 vivado_path: Path to Vivado executable. Can set to ``None`` 

27 to use whatever version is in ``PATH``. 

28 tcl_file: Path to TCL file. 

29 no_log_file: Optionally set Vivado flags to not create log and journal files. 

30 

31 Return: 

32 True if everything went well. 

33 """ 

34 tcl_file = tcl_file.resolve() 

35 

36 cmd = [ 

37 str(get_vivado_path(vivado_path)), 

38 "-mode", 

39 "batch", 

40 "-notrace", 

41 "-source", 

42 str(tcl_file), 

43 ] 

44 if no_log_file: 

45 cmd += ["-nojournal", "-nolog"] 

46 

47 try: 

48 Process(args=cmd, cwd=tcl_file.parent).consume_output() 

49 except Process.NonZeroExitCode: 

50 return False 

51 return True 

52 

53 

54def run_vivado_gui(vivado_path: Path | None, project_file: Path) -> bool: 

55 """ 

56 Setting cwd ensures that any .log or .jou files produced are placed in 

57 the same directory as the project. 

58 

59 Arguments: 

60 vivado_path: Path to Vivado executable. 

61 Leave as ``None`` to use whatever is available in the system ``PATH``. 

62 project_file: Path to a project .xpr file. 

63 

64 Return: 

65 True if everything went well. 

66 """ 

67 project_file = project_file.resolve() 

68 if not project_file.exists(): 

69 raise FileNotFoundError(f"Project does not exist: {project_file}") 

70 

71 cmd = [str(get_vivado_path(vivado_path)), "-mode", "gui", str(project_file)] 

72 

73 try: 

74 Process(args=cmd, cwd=project_file.parent).consume_output() 

75 except Process.NonZeroExitCode: 

76 return False 

77 return True 

78 

79 

80def get_vivado_path(vivado_path: Path | None = None) -> Path: 

81 """ 

82 Wrapper to get a path to Vivado executable. 

83 

84 Arguments: 

85 vivado_path: Path to vivado executable. 

86 Leave as ``None`` to use whatever is available in the system ``PATH``. 

87 """ 

88 if vivado_path is not None: 

89 return vivado_path.resolve() 

90 

91 which_vivado = which("vivado") 

92 if which_vivado is None: 

93 raise FileNotFoundError("Could not find vivado on PATH") 

94 

95 return Path(which_vivado).resolve() 

96 

97 

98def get_vivado_version(vivado_path: Path | None = None) -> str: 

99 """ 

100 Get the version number of the Vivado installation. 

101 

102 Arguments: 

103 vivado_path: Path to vivado executable. 

104 Leave as ``None`` to use whatever is available in the system ``PATH``. 

105 

106 Return: 

107 The version, e.g. ``"2021.2"``. 

108 """ 

109 vivado_path = get_vivado_path(vivado_path=vivado_path) 

110 

111 # E.g. "/home/lukas/work/Xilinx/Vivado/2021.2/bin/vivado" -> "2021.2" 

112 return vivado_path.parent.parent.name 

113 

114 

115def get_git_sha_slv(git_directory: Path) -> tuple[str, str]: 

116 """ 

117 Get the current git SHA encoded as 32-character binary strings. Suitable for setting 

118 as ``std_logic_vector`` generics to a Vivado build, where they can be assigned to 

119 software-accessible registers to keep track of what revision an FPGA was built from. 

120 

121 Will return the left-most 16 characters of the git SHA, encoded into two 32-character 

122 binary strings. 

123 

124 Arguments: 

125 git_directory: The directory where git commands will be run. 

126 

127 Return: 

128 First object in tuple is the left-most eight characters of the git SHA 

129 encoded as a 32-character binary string. 

130 Second object is the next eight characters from the git SHA. 

131 """ 

132 git_sha = get_git_sha(directory=git_directory) 

133 if len(git_sha) != 16: 

134 raise ValueError("Wrong string length") 

135 

136 def hex_to_binary_string(hex_string: str) -> str: 

137 if len(hex_string) != 8: 

138 raise ValueError("Wrong string length") 

139 int_value = int(hex_string, base=16) 

140 

141 return to_binary_string(value=int_value, result_width=32) 

142 

143 left_binary_string = hex_to_binary_string(hex_string=git_sha[0:8]) 

144 right_binary_string = hex_to_binary_string(hex_string=git_sha[8:16]) 

145 

146 return (left_binary_string, right_binary_string) 

147 

148 

149def to_tcl_path(path: Path) -> str: 

150 """ 

151 Return a path string in a format suitable for TCL. 

152 """ 

153 return str(path.resolve()).replace("\\", "/")