Coverage for tsfpga/vivado/simlib_common.py: 91%
66 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-01 20:51 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-01 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# --------------------------------------------------------------------------------------------------
9from __future__ import annotations
11import platform
12import zipfile
13from abc import ABC, abstractmethod
14from shutil import make_archive
15from typing import TYPE_CHECKING
17from tsfpga.system_utils import create_file, delete
18from tsfpga.vivado.common import get_vivado_version
20from .common import get_vivado_path
22if TYPE_CHECKING:
23 from pathlib import Path
26class VivadoSimlibCommon(ABC):
27 """
28 Class for handling Vivado simlib used for simulation. Keeps track of when a
29 (re)compile is needed.
31 This is a base class that defines an interface and some common methods.
32 See subclasses for details: :class:`.VivadoSimlibGhdl`, :class:`.VivadoSimlibCommercial`.
33 """
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
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]
43 def __init__(self, vivado_path: Path | None, output_path: Path) -> None:
44 """
45 Call from subclass. 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()
50 self.output_path = output_path.resolve() / self._get_version_tag()
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
61 return False
63 @property
64 def compile_is_needed(self) -> bool:
65 """
66 If there is compiled simlib available that matches
68 * Operating system
69 * Vivado version
70 * Simulator version
72 then there should not be a recompile.
74 .. note::
75 Subclass implementations might add further conditions.
77 Return:
78 True if compiled simlib is not available. False otherwise.
79 """
80 return not self._done_token.exists()
82 def compile(self) -> None:
83 """
84 Compile simlib.
85 """
86 # Delete any existing artifacts, which might be fully or partially compiled.
87 # This also deletes the "done" token file if it exists.
88 # Specifically GHDL compilation fails if there are existing compiled artifacts
89 delete(self.output_path)
91 print(f"Compiling Vivado simlib from {self._libraries_path} into {self.output_path}...")
92 self._compile()
94 create_file(self._done_token, "Done!")
96 @abstractmethod
97 def _compile(self) -> None:
98 """
99 Compile simlib.
100 Overload in a subclass.
101 """
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()
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 """
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
125 def to_archive(self) -> Path:
126 """
127 Compress compiled simlib to an archive.
129 Return:
130 Path to the archive.
131 """
132 make_archive(str(self.output_path), "zip", self.output_path)
133 return self.output_path.parent / (self.output_path.name + ".zip")
135 def from_archive(self, archive: Path) -> None:
136 """
137 Unpack compiled simlib from an existing archive.
139 Arguments:
140 archive: Path to a zip archive with previously compiled simlib.
141 """
142 with zipfile.ZipFile(archive, "r") as zip_handle:
143 zip_handle.extractall(self.output_path)
145 def _get_version_tag(self) -> str:
146 tag = "vivado-simlib-"
147 tag += self._get_operating_system_tag()
148 tag += f".{self._get_vivado_version_tag()}"
149 tag += f".{self._get_simulator_tag()}"
150 tag += f".format_{self._format_version_id}"
152 return tag
154 def _get_operating_system_tag(self) -> str:
155 """
156 Return e.g. "linux".
157 """
158 return self._format_version(platform.system())
160 def _get_vivado_version_tag(self) -> str:
161 """
162 Return e.g. "vivado_2021_2".
163 """
164 vivado_version = get_vivado_version(self._vivado_path)
166 return self._format_version(f"vivado_{vivado_version}")
168 @abstractmethod
169 def _get_simulator_tag(self) -> str:
170 """
171 Return a simulator tag as a string, e.g. "ghdl_1.2.3".
172 Overload in a subclass.
173 """
175 @staticmethod
176 def _format_version(version: str) -> str:
177 """
178 Format version string to something suitable for artifactory versioning.
179 """
180 return version.replace(".", "_").replace("-", "_").lower()
182 @property
183 def _done_token(self) -> Path:
184 """
185 Path to "done" token file.
186 """
187 return self.output_path / "done.txt"