Coverage for tsfpga/vivado/simlib_open_source.py: 47%
73 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-27 20:51 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-27 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
11from abc import abstractmethod
12from typing import TYPE_CHECKING, ClassVar
14from tsfpga import DEFAULT_FILE_ENCODING
15from tsfpga.system_utils import create_directory, system_is_windows
17from .simlib_common import VivadoSimlibCommon
19if TYPE_CHECKING:
20 from pathlib import Path
23class VivadoSimlibOpenSource(VivadoSimlibCommon):
24 """
25 Common methods for handling Vivado simlib with an open-source simulator.
26 See subclasses for details: :class:`.VivadoSimlibGhdl`, :class:`.VivadoSimlibNvc`.
28 Do not instantiate this class directly.
29 Use factory class :class:`.VivadoSimlib` instead.
30 """
32 library_names: ClassVar = ["unisim", "secureip", "unimacro", "unifast"]
34 # Set in subclass.
35 _create_library_folder_before_compile: bool
37 def _compile(self) -> None:
38 self._compile_unisim()
39 self._compile_secureip()
40 self._compile_unimacro()
41 self._compile_unifast()
43 def _compile_unisim(self) -> None:
44 library_path = self._libraries_path / "unisims"
46 vhd_files = []
48 for vhd_file_base in [
49 "unisim_VPKG",
50 "unisim_retarget_VCOMP",
51 ]:
52 vhd_file = library_path / f"{vhd_file_base}.vhd"
53 if not vhd_file.exists():
54 raise FileNotFoundError(f"VHDL file does not exist: {vhd_file}")
56 vhd_files.append(vhd_file)
58 primitive_dir = library_path / "primitive"
59 vhd_files += self._get_compile_order(library_path=primitive_dir)
61 retarget_dir = library_path / "retarget"
62 for vhd_file in retarget_dir.glob("*.vhd"):
63 vhd_files.append(vhd_file)
65 self._compile_library(vhd_files=vhd_files, library_name="unisim")
67 def _compile_secureip(self) -> None:
68 vhd_files = list((self._libraries_path / "unisims" / "secureip").glob("*.vhd"))
69 self._compile_library(vhd_files=vhd_files, library_name="secureip")
71 def _compile_unimacro(self) -> None:
72 library_path = self._libraries_path / "unimacro"
74 vhd_files = []
76 vhd_file = library_path / "unimacro_VCOMP.vhd"
77 if not vhd_file.exists():
78 raise FileNotFoundError(f"VHDL file does not exist: {vhd_file}")
79 vhd_files.append(vhd_file)
81 vhd_files += self._get_compile_order(library_path=library_path)
83 self._compile_library(vhd_files=vhd_files, library_name="unimacro")
85 def _compile_unifast(self) -> None:
86 library_path = self._libraries_path / "unifast" / "primitive"
87 vhd_files = self._get_compile_order(library_path=library_path)
89 self._compile_library(vhd_files=vhd_files, library_name="unifast")
91 @staticmethod
92 def _get_compile_order(library_path: Path) -> list[Path]:
93 """
94 Get compile order (list of file paths, in order) from an existing compile order
95 file provided by Xilinx.
96 """
97 vhd_files = []
99 with (library_path / "vhdl_analyze_order").open(
100 encoding=DEFAULT_FILE_ENCODING
101 ) as file_handle:
102 for vhd_file_base in file_handle:
103 vhd_file = library_path / vhd_file_base.strip()
104 if not vhd_file.exists():
105 raise FileNotFoundError(f"VHDL file does not exist: {vhd_file}")
107 vhd_files.append(vhd_file)
109 return vhd_files
111 def _compile_library(self, vhd_files: list[Path], library_name: str) -> None:
112 """
113 Compile all files of the library.
114 """
115 output_path = self.output_path / library_name
116 if self._create_library_folder_before_compile:
117 create_directory(output_path, empty=True)
119 # There seems to be a command length limit on Windows.
120 # While compiling all files in one command gives a huge performance boost
121 # (on Linux at least, as far as we know) the resulting command is in the order of
122 # 90k characters long.
123 # This does not seem to work on Windows.
124 # So we auto detect the OS to work around this limitation, while keeping the performance
125 # boost on Linux.
126 compile_file_by_file = system_is_windows()
128 if compile_file_by_file:
129 # Compile each file in a separate command.
130 for vhd_file in vhd_files:
131 self._print_and_compile(
132 output_path=output_path, library_name=library_name, vhd_files=[vhd_file]
133 )
135 else:
136 # Compile all files in one single command.
137 self._print_and_compile(
138 output_path=output_path, library_name=library_name, vhd_files=vhd_files
139 )
141 def _print_and_compile(
142 self, output_path: Path, library_name: str, vhd_files: list[Path]
143 ) -> None:
144 """
145 Print the file list and compile it.
146 """
147 vhd_paths_str = [str(vhd_file) for vhd_file in vhd_files]
149 # Use paths relative to the Vivado installation, in order to keep it a little shorter
150 # (it is still massively long).
151 vhd_paths_relative = [
152 str(vhd_file.relative_to(self._libraries_path)) for vhd_file in vhd_files
153 ]
154 paths_to_print = ", ".join(vhd_paths_relative)
155 print(f"Compiling {paths_to_print} into {library_name}...")
157 self._execute_compile(
158 output_path=output_path, library_name=library_name, vhd_files=vhd_paths_str
159 )
161 @abstractmethod
162 def _execute_compile(self, output_path: Path, library_name: str, vhd_files: list[str]) -> None:
163 """
164 Compile the files into the specified library.
165 Raise exception upon failure.
166 """