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

49 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-20 20:52 +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 

9# Standard libraries 

10from pathlib import Path 

11from shutil import which 

12from typing import Optional 

13 

14# Third party libraries 

15from vunit.ostools import Process 

16 

17# First party libraries 

18from tsfpga.git_utils import get_git_sha 

19from tsfpga.math_utils import to_binary_string 

20 

21 

22def run_vivado_tcl(vivado_path: Optional[Path], tcl_file: Path, no_log_file: bool = False) -> bool: 

23 """ 

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

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

26 

27 Arguments: 

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

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

30 tcl_file: Path to TCL file. 

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

32 

33 Return: 

34 True if everything went well. 

35 """ 

36 tcl_file = tcl_file.resolve() 

37 

38 cmd = [ 

39 str(get_vivado_path(vivado_path)), 

40 "-mode", 

41 "batch", 

42 "-notrace", 

43 "-source", 

44 str(tcl_file), 

45 ] 

46 if no_log_file: 

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

48 

49 try: 

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

51 except Process.NonZeroExitCode: 

52 return False 

53 return True 

54 

55 

56def run_vivado_gui(vivado_path: Optional[Path], project_file: Path) -> bool: 

57 """ 

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

59 the same directory as the project. 

60 

61 Arguments: 

62 vivado_path: Path to Vivado executable. 

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

64 project_file: Path to a project .xpr file. 

65 

66 Return: 

67 True if everything went well. 

68 """ 

69 project_file = project_file.resolve() 

70 if not project_file.exists(): 

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

72 

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

74 

75 try: 

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

77 except Process.NonZeroExitCode: 

78 return False 

79 return True 

80 

81 

82def get_vivado_path(vivado_path: Optional[Path] = None) -> Path: 

83 """ 

84 Wrapper to get a path to Vivado executable. 

85 

86 Arguments: 

87 vivado_path: Path to vivado executable. 

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

89 """ 

90 if vivado_path is not None: 

91 return vivado_path.resolve() 

92 

93 which_vivado = which("vivado") 

94 if which_vivado is None: 

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

96 

97 return Path(which_vivado).resolve() 

98 

99 

100def get_vivado_version(vivado_path: Optional[Path] = None) -> str: 

101 """ 

102 Get the version number of the Vivado installation. 

103 

104 Arguments: 

105 vivado_path: Path to vivado executable. 

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

107 

108 Return: 

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

110 """ 

111 vivado_path = get_vivado_path(vivado_path=vivado_path) 

112 

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

114 vivado_version = vivado_path.parent.parent.name 

115 

116 return vivado_version 

117 

118 

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

120 """ 

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

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

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

124 

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

126 binary strings. 

127 

128 Arguments: 

129 directory: The directory where git commands will be run. 

130 

131 Return: 

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

133 encoded as a 32-character binary string. 

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

135 """ 

136 git_sha = get_git_sha(directory=git_directory) 

137 assert len(git_sha) == 16 

138 

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

140 assert len(hex_string) == 8 

141 int_value = int(hex_string, base=16) 

142 

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

144 

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

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

147 

148 return (left_binary_string, right_binary_string) 

149 

150 

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

152 """ 

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

154 """ 

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