Coverage for tsfpga/vivado/simlib_ghdl.py: 52%
94 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-10 20:51 +0000
« 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# --------------------------------------------------------------------------------------------------
9# Standard libraries
10import re
11from pathlib import Path
12from typing import Any, Optional
14# First party libraries
15from tsfpga import DEFAULT_FILE_ENCODING
16from tsfpga.system_utils import create_directory, run_command, system_is_windows
18# Local folder libraries
19from .simlib_common import VivadoSimlibCommon
22class VivadoSimlibGhdl(VivadoSimlibCommon):
24 """
25 Handle Vivado simlib with GHDL.
26 """
28 library_names = ["unisim", "secureip", "unimacro", "unifast"]
30 def __init__(
31 self,
32 vivado_path: Optional[Path],
33 output_path: Path,
34 vunit_proj: Any,
35 simulator_interface: Any,
36 ) -> None:
37 """
38 Arguments:
39 output_path: The compiled simlib will be placed here.
40 vunit_proj: The VUnit project that is used to run simulation.
41 simulator_interface: A VUnit SimulatorInterface class.
42 vivado_path: Path to Vivado executable.
43 """
44 self.ghdl_binary = Path(simulator_interface.find_prefix()) / "ghdl"
46 super().__init__(vivado_path=vivado_path, output_path=output_path)
48 self._vunit_proj = vunit_proj
50 def _compile(self) -> None:
51 self._compile_unisim()
52 self._compile_secureip()
53 self._compile_unimacro()
54 self._compile_unifast()
56 def _compile_unisim(self) -> None:
57 library_path = self._libraries_path / "unisims"
59 vhd_files = []
61 for vhd_file_base in [
62 "unisim_VPKG",
63 "unisim_retarget_VCOMP",
64 ]:
65 vhd_file = library_path / f"{vhd_file_base}.vhd"
66 assert vhd_file.exists(), vhd_file
68 vhd_files.append(vhd_file)
70 primitive_dir = library_path / "primitive"
71 vhd_files += self._get_compile_order(library_path=primitive_dir)
73 retarget_dir = library_path / "retarget"
74 for vhd_file in retarget_dir.glob("*.vhd"):
75 vhd_files.append(vhd_file)
77 self._compile_ghdl(vhd_files=vhd_files, library_name="unisim")
79 def _compile_secureip(self) -> None:
80 library_path = self._libraries_path / "unisims" / "secureip"
82 vhd_files = []
83 for vhd_file in library_path.glob("*.vhd"):
84 vhd_files.append(vhd_file)
86 self._compile_ghdl(vhd_files=vhd_files, library_name="secureip")
88 def _compile_unimacro(self) -> None:
89 library_path = self._libraries_path / "unimacro"
91 vhd_files = []
93 vhd_file = library_path / "unimacro_VCOMP.vhd"
94 assert vhd_file.exists(), vhd_file
95 vhd_files.append(vhd_file)
97 vhd_files += self._get_compile_order(library_path=library_path)
99 self._compile_ghdl(vhd_files=vhd_files, library_name="unimacro")
101 def _compile_unifast(self) -> None:
102 library_path = self._libraries_path / "unifast" / "primitive"
103 vhd_files = self._get_compile_order(library_path=library_path)
105 self._compile_ghdl(vhd_files=vhd_files, library_name="unifast")
107 @staticmethod
108 def _get_compile_order(library_path: Path) -> list[Path]:
109 """
110 Get compile order (list of file paths, in order) from an existing compile order
111 file provided by Xilinx.
112 """
113 vhd_files = []
115 with open(
116 library_path / "vhdl_analyze_order", encoding=DEFAULT_FILE_ENCODING
117 ) as file_handle:
118 for vhd_file_base in file_handle.readlines():
119 vhd_file = library_path / vhd_file_base.strip()
120 assert vhd_file.exists(), vhd_file
121 vhd_files.append(vhd_file)
123 return vhd_files
125 def _compile_ghdl(self, vhd_files: list[Path], library_name: str) -> None:
126 """
127 Compile a list of files into the specified library.
128 """
129 workdir = self.output_path / library_name
130 create_directory(workdir, empty=False)
132 vhd_paths_str = [str(vhd_file) for vhd_file in vhd_files]
133 # We print a list of the files that will be compiled.
134 # Use relative paths to the Vivado path, in order to keep it a little shorter
135 # (it is still massively long).
136 vhd_paths_relative = [
137 str(vhd_file.relative_to(self._libraries_path)) for vhd_file in vhd_files
138 ]
140 def print_compiling(path: str) -> None:
141 print(f"Compiling {path} into {library_name}...")
143 def execute_ghdl(files: list[str]) -> None:
144 self._execute_ghdl(workdir=workdir, library_name=library_name, files=files)
146 # There seems to be a command length limit on Windows.
147 # While compiling all files in one command gives a huge performance boost
148 # (on Linux with GCC backend at least, as far as we know) the resulting command is in
149 # the order of 90k characters long.
150 # This does not seem to work on Windows.
151 # So we auto detect the OS to work around this limitation, while keeping the performance
152 # boost on Linux.
153 # See https://gitlab.com/tsfpga/tsfpga/-/merge_requests/499
154 compile_file_by_file = system_is_windows()
156 if compile_file_by_file:
157 # Compile each file in a separate command.
158 for vhd_file_idx, vhd_file_str in enumerate(vhd_paths_str):
159 print_compiling(vhd_paths_relative[vhd_file_idx])
160 execute_ghdl(files=[vhd_file_str])
162 else:
163 # Compile all files in one single command.
164 paths_to_print = ", ".join(vhd_paths_relative)
165 print_compiling(paths_to_print)
166 execute_ghdl(files=vhd_paths_str)
168 def _execute_ghdl(self, workdir: Path, library_name: str, files: list[str]) -> None:
169 cmd = [
170 str(self.ghdl_binary),
171 "-a",
172 "--ieee=synopsys",
173 "--std=08",
174 f"--workdir={str(workdir.resolve())}",
175 f"-P{str(self.output_path / 'unisim')}",
176 "-fexplicit",
177 "-frelaxed-rules",
178 "--no-vital-checks",
179 "--warn-binding",
180 "--mb-comments",
181 f"--work={library_name}",
182 ] + files
184 run_command(cmd, cwd=self.output_path)
186 def _get_simulator_tag(self) -> str:
187 """
188 Return simulator version tag as a string.
189 """
190 cmd = [str(self.ghdl_binary), "--version"]
191 output = run_command(cmd, capture_output=True).stdout
193 regexp_with_tag = re.compile(r"^GHDL (\S+) \((\S+)\).*")
194 match = regexp_with_tag.search(output)
195 if match is not None:
196 return self._format_version(f"ghdl_{match.group(1)}_{match.group(2)}")
198 regexp_without_tag = re.compile(r"^GHDL (\S+).*")
199 match = regexp_without_tag.search(output)
200 if match is not None:
201 return self._format_version(f"ghdl_{match.group(1)}")
203 raise ValueError(f"Could not find GHDL version string: {output}")
205 def _add_to_vunit_project(self) -> None:
206 """
207 Add the compiled simlib to your VUnit project.
208 """
209 for library_name in self.library_names:
210 library_path = self.output_path / library_name
211 assert library_path.exists(), library_path
213 self._vunit_proj.add_external_library(library_name, library_path)