Coverage for tsfpga/vivado/simlib_common.py: 91%

67 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-07 11:31 +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 

10import platform 

11import zipfile 

12from abc import ABC, abstractmethod 

13from pathlib import Path 

14from shutil import make_archive 

15from typing import Optional 

16 

17# First party libraries 

18from tsfpga.system_utils import create_file, delete 

19from tsfpga.vivado.common import get_vivado_version 

20 

21# Local folder libraries 

22from .common import get_vivado_path 

23 

24 

25class VivadoSimlibCommon(ABC): 

26 """ 

27 Class for handling Vivado simlib used for simulation. Keeps track of when a 

28 (re)compile is needed. 

29 

30 This is a base class that defines an interface and some common methods. 

31 See subclasses for details: :class:`.VivadoSimlibGhdl`, :class:`.VivadoSimlibCommercial`. 

32 """ 

33 

34 # The version of this class. Can be bumped to force a re-compile if e.g. the TCL script changes 

35 # or the output folder structure is updated. 

36 _format_version_id = 4 

37 

38 # Set in subclass to a list of strings. The libraries that shall be compiled and added to 

39 # VUnit project. 

40 library_names: list[str] 

41 

42 def __init__(self, vivado_path: Optional[Path], output_path: Path) -> None: 

43 """ 

44 Call from subclass. Please do not instantiate this class directly. 

45 """ 

46 self._vivado_path = get_vivado_path(vivado_path) 

47 self._libraries_path = (self._vivado_path.parent.parent / "data" / "vhdl" / "src").resolve() 

48 

49 self.output_path = output_path.resolve() / self._get_version_tag() 

50 

51 def compile_if_needed(self) -> bool: 

52 """ 

53 Compile if needed (if :meth:`compile_is_needed <.compile_is_needed>` condition is not 

54 fulfilled). 

55 """ 

56 if self.compile_is_needed: 

57 self.compile() 

58 return True 

59 

60 return False 

61 

62 @property 

63 def compile_is_needed(self) -> bool: 

64 """ 

65 If there is compiled simlib available that matches 

66 

67 * Operating system 

68 * Vivado version 

69 * Simulator version 

70 

71 then there should not be a recompile. 

72 

73 .. note:: 

74 Subclass implementations might add further conditions. 

75 

76 Return: 

77 True if compiled simlib is not available. False otherwise. 

78 """ 

79 if self._done_token.exists(): 

80 return False 

81 

82 return True 

83 

84 def compile(self) -> None: 

85 """ 

86 Compile simlib. 

87 """ 

88 # Probably does not exists, but try to delete just in case. 

89 delete(self._done_token) 

90 

91 print(f"Compiling Vivado simlib from {self._libraries_path} into {self.output_path}...") 

92 self._compile() 

93 

94 create_file(self._done_token, "Done!") 

95 

96 @abstractmethod 

97 def _compile(self) -> None: 

98 """ 

99 Compile simlib. 

100 Overload in a subclass. 

101 """ 

102 

103 def add_to_vunit_project(self) -> None: 

104 """ 

105 Add the compiled simlib to your VUnit project. 

106 """ 

107 self._add_to_vunit_project() 

108 

109 @abstractmethod 

110 def _add_to_vunit_project(self) -> None: 

111 """ 

112 Add the compiled simlib to your VUnit project. 

113 Overload in a subclass. 

114 """ 

115 

116 @property 

117 def artifact_name(self) -> str: 

118 """ 

119 The name of the folder where simlib is or will be compiled. 

120 Follows a format ``vivado-simlib-WW.XX.YY.ZZ`` suitable for storage and versioning 

121 in Artifactory. 

122 """ 

123 return self.output_path.name 

124 

125 def to_archive(self) -> Path: 

126 """ 

127 Compress compiled simlib to an archive. 

128 

129 Return: 

130 Path to the archive. 

131 """ 

132 make_archive(str(self.output_path), "zip", self.output_path) 

133 archive = self.output_path.parent / (self.output_path.name + ".zip") 

134 

135 return archive 

136 

137 def from_archive(self, archive: Path) -> None: 

138 """ 

139 Unpack compiled simlib from an existing archive. 

140 

141 Arguments: 

142 archive (pathlib.Path): Path to a zip archive with previously compiled simlib. 

143 """ 

144 with zipfile.ZipFile(archive, "r") as zip_handle: 

145 zip_handle.extractall(self.output_path) 

146 

147 def _get_version_tag(self) -> str: 

148 tag = "vivado-simlib-" 

149 tag += self._get_operating_system_tag() 

150 tag += f".{self._get_vivado_version_tag()}" 

151 tag += f".{self._get_simulator_tag()}" 

152 tag += f".format_{self._format_version_id}" 

153 

154 return tag 

155 

156 def _get_operating_system_tag(self) -> str: 

157 """ 

158 Return e.g. "linux". 

159 """ 

160 return self._format_version(platform.system()) 

161 

162 def _get_vivado_version_tag(self) -> str: 

163 """ 

164 Return e.g. "vivado_2021_2". 

165 """ 

166 vivado_version = get_vivado_version(self._vivado_path) 

167 

168 return self._format_version(f"vivado_{vivado_version}") 

169 

170 @abstractmethod 

171 def _get_simulator_tag(self) -> str: 

172 """ 

173 Return a simulator tag as a string, e.g. "ghdl_1.2.3". 

174 Overload in a subclass. 

175 """ 

176 

177 @staticmethod 

178 def _format_version(version: str) -> str: 

179 """ 

180 Format version string to something suitable for artifactory versioning. 

181 """ 

182 return version.replace(".", "_").replace("-", "_").lower() 

183 

184 @property 

185 def _done_token(self) -> Path: 

186 """ 

187 Path to "done" token file. 

188 """ 

189 return self.output_path / "done.txt"