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

67 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-10 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 

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

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

29 (re)compile is needed. 

30 

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

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

33 """ 

34 

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

36 # or the output folder structure is updated. 

37 _format_version_id = 4 

38 

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

40 # VUnit project. 

41 library_names: list[str] 

42 

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

44 """ 

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

46 """ 

47 self._vivado_path = get_vivado_path(vivado_path) 

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

49 

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

51 

52 def compile_if_needed(self) -> bool: 

53 """ 

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

55 fulfilled). 

56 """ 

57 if self.compile_is_needed: 

58 self.compile() 

59 return True 

60 

61 return False 

62 

63 @property 

64 def compile_is_needed(self) -> bool: 

65 """ 

66 If there is compiled simlib available that matches 

67 

68 * Operating system 

69 * Vivado version 

70 * Simulator version 

71 

72 then there should not be a recompile. 

73 

74 .. note:: 

75 Subclass implementations might add further conditions. 

76 

77 Return: 

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

79 """ 

80 if self._done_token.exists(): 

81 return False 

82 

83 return True 

84 

85 def compile(self) -> None: 

86 """ 

87 Compile simlib. 

88 """ 

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

90 delete(self._done_token) 

91 

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

93 self._compile() 

94 

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

96 

97 @abstractmethod 

98 def _compile(self) -> None: 

99 """ 

100 Compile simlib. 

101 Overload in a subclass. 

102 """ 

103 

104 def add_to_vunit_project(self) -> None: 

105 """ 

106 Add the compiled simlib to your VUnit project. 

107 """ 

108 self._add_to_vunit_project() 

109 

110 @abstractmethod 

111 def _add_to_vunit_project(self) -> None: 

112 """ 

113 Add the compiled simlib to your VUnit project. 

114 Overload in a subclass. 

115 """ 

116 

117 @property 

118 def artifact_name(self) -> str: 

119 """ 

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

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

122 in Artifactory. 

123 """ 

124 return self.output_path.name 

125 

126 def to_archive(self) -> Path: 

127 """ 

128 Compress compiled simlib to an archive. 

129 

130 Return: 

131 Path to the archive. 

132 """ 

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

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

135 

136 return archive 

137 

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

139 """ 

140 Unpack compiled simlib from an existing archive. 

141 

142 Arguments: 

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

144 """ 

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

146 zip_handle.extractall(self.output_path) 

147 

148 def _get_version_tag(self) -> str: 

149 tag = "vivado-simlib-" 

150 tag += self._get_operating_system_tag() 

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

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

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

154 

155 return tag 

156 

157 def _get_operating_system_tag(self) -> str: 

158 """ 

159 Return e.g. "linux". 

160 """ 

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

162 

163 def _get_vivado_version_tag(self) -> str: 

164 """ 

165 Return e.g. "vivado_2021_2". 

166 """ 

167 vivado_version = get_vivado_version(self._vivado_path) 

168 

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

170 

171 @abstractmethod 

172 def _get_simulator_tag(self) -> str: 

173 """ 

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

175 Overload in a subclass. 

176 """ 

177 

178 @staticmethod 

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

180 """ 

181 Format version string to something suitable for artifactory versioning. 

182 """ 

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

184 

185 @property 

186 def _done_token(self) -> Path: 

187 """ 

188 Path to "done" token file. 

189 """ 

190 return self.output_path / "done.txt"