Coverage for tsfpga/vivado/simlib_common.py: 91%
67 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-07 20:51 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-07 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# --------------------------------------------------------------------------------------------------
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
17# First party libraries
18from tsfpga.system_utils import create_file, delete
19from tsfpga.vivado.common import get_vivado_version
21# Local folder libraries
22from .common import get_vivado_path
25class VivadoSimlibCommon(ABC):
26 """
27 Class for handling Vivado simlib used for simulation. Keeps track of when a
28 (re)compile is needed.
30 This is a base class that defines an interface and some common methods.
31 See subclasses for details: :class:`.VivadoSimlibGhdl`, :class:`.VivadoSimlibCommercial`.
32 """
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
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]
42 def __init__(self, vivado_path: Optional[Path], output_path: Path) -> None:
43 """
44 Call from subclass. 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()
49 self.output_path = output_path.resolve() / self._get_version_tag()
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
60 return False
62 @property
63 def compile_is_needed(self) -> bool:
64 """
65 If there is compiled simlib available that matches
67 * Operating system
68 * Vivado version
69 * Simulator version
71 then there should not be a recompile.
73 .. note::
74 Subclass implementations might add further conditions.
76 Return:
77 True if compiled simlib is not available. False otherwise.
78 """
79 if self._done_token.exists():
80 return False
82 return True
84 def compile(self) -> None:
85 """
86 Compile simlib.
87 """
88 # Delete any existing artifacts, which might be fully or partially compiled.
89 # This also deletes the "done" token file if it exists.
90 # Specifically GHDL compilation fails if there are existing compiled artifacts
91 delete(self.output_path)
93 print(f"Compiling Vivado simlib from {self._libraries_path} into {self.output_path}...")
94 self._compile()
96 create_file(self._done_token, "Done!")
98 @abstractmethod
99 def _compile(self) -> None:
100 """
101 Compile simlib.
102 Overload in a subclass.
103 """
105 def add_to_vunit_project(self) -> None:
106 """
107 Add the compiled simlib to your VUnit project.
108 """
109 self._add_to_vunit_project()
111 @abstractmethod
112 def _add_to_vunit_project(self) -> None:
113 """
114 Add the compiled simlib to your VUnit project.
115 Overload in a subclass.
116 """
118 @property
119 def artifact_name(self) -> str:
120 """
121 The name of the folder where simlib is or will be compiled.
122 Follows a format ``vivado-simlib-WW.XX.YY.ZZ`` suitable for storage and versioning
123 in Artifactory.
124 """
125 return self.output_path.name
127 def to_archive(self) -> Path:
128 """
129 Compress compiled simlib to an archive.
131 Return:
132 Path to the archive.
133 """
134 make_archive(str(self.output_path), "zip", self.output_path)
135 archive = self.output_path.parent / (self.output_path.name + ".zip")
137 return archive
139 def from_archive(self, archive: Path) -> None:
140 """
141 Unpack compiled simlib from an existing archive.
143 Arguments:
144 archive: Path to a zip archive with previously compiled simlib.
145 """
146 with zipfile.ZipFile(archive, "r") as zip_handle:
147 zip_handle.extractall(self.output_path)
149 def _get_version_tag(self) -> str:
150 tag = "vivado-simlib-"
151 tag += self._get_operating_system_tag()
152 tag += f".{self._get_vivado_version_tag()}"
153 tag += f".{self._get_simulator_tag()}"
154 tag += f".format_{self._format_version_id}"
156 return tag
158 def _get_operating_system_tag(self) -> str:
159 """
160 Return e.g. "linux".
161 """
162 return self._format_version(platform.system())
164 def _get_vivado_version_tag(self) -> str:
165 """
166 Return e.g. "vivado_2021_2".
167 """
168 vivado_version = get_vivado_version(self._vivado_path)
170 return self._format_version(f"vivado_{vivado_version}")
172 @abstractmethod
173 def _get_simulator_tag(self) -> str:
174 """
175 Return a simulator tag as a string, e.g. "ghdl_1.2.3".
176 Overload in a subclass.
177 """
179 @staticmethod
180 def _format_version(version: str) -> str:
181 """
182 Format version string to something suitable for artifactory versioning.
183 """
184 return version.replace(".", "_").replace("-", "_").lower()
186 @property
187 def _done_token(self) -> Path:
188 """
189 Path to "done" token file.
190 """
191 return self.output_path / "done.txt"