Coverage for tsfpga/examples/vivado/project.py: 0%

39 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-18 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 

9import datetime 

10import platform 

11import random 

12from pathlib import Path 

13from typing import TYPE_CHECKING, Any 

14 

15from tsfpga import REPO_ROOT 

16from tsfpga.git_utils import get_git_commit 

17from tsfpga.vivado.common import get_vivado_version 

18from tsfpga.vivado.project import VivadoNetlistProject, VivadoProject 

19 

20if TYPE_CHECKING: 

21 from hdl_registers.register_list import RegisterList 

22 

23THIS_DIR = Path(__file__).parent 

24 

25 

26class TsfpgaExampleVivadoProject(VivadoProject): 

27 """ 

28 Example Vivado project class. 

29 Shows how to override and extend the base behavior. 

30 """ 

31 

32 def pre_create( 

33 self, 

34 generics: dict[str, Any], 

35 **kwargs: Any, # noqa: ANN401 

36 ) -> bool: 

37 """ 

38 Is called right before the Vivado system call that creates the project. 

39 Override parent method to add custom behavior. 

40 """ 

41 # Add script that elevates the severity of even more Vivado messages, to get more 

42 # quality feedback from the tool. 

43 self.tcl_sources.append(THIS_DIR / "tcl" / "example_vivado_messages.tcl") 

44 

45 # Set 'build_id', which is a generic without a default value, so it is possible to 

46 # build the project in the GUI. 

47 # In a proper command-line build, it will be overridden again in 'pre_build'. 

48 # See 'pre_build' for more information. 

49 self._set_build_id_generic(generics=generics) 

50 

51 return super().pre_create(generics=generics, kwargs=kwargs) 

52 

53 def pre_build( 

54 self, 

55 generics: dict[str, Any], 

56 **kwargs: Any, # noqa: ANN401 

57 ) -> bool: 

58 """ 

59 Is called right before the Vivado system call that builds the project. 

60 Override parent method to add custom behavior. 

61 

62 Note that this function is called after generating register HDL artifacts in the 

63 build flow. 

64 Hence, the register constants we set will only be available in the generated software code, 

65 not in the HDL. 

66 Hence we can run many builds in parallel, without having race conditions between the 

67 different values in HDL on the filesystem. 

68 """ 

69 self._set_build_id_generic(generics=generics) 

70 self._set_build_register_constants(generics=generics) 

71 

72 return super().pre_build(generics=generics, kwargs=kwargs) 

73 

74 def _set_build_id_generic(self, generics: dict[str, Any]) -> None: 

75 """ 

76 Set a random value. 

77 """ 

78 # Set a suitable range so the generic can be handled as a VHDL 'natural'. 

79 # Does not need to be cryptographically secure. 

80 generics["build_id"] = random.randint(1, 2**25 - 1) # noqa: S311 

81 

82 def _set_build_register_constants(self, generics: dict[str, Any]) -> None: 

83 """ 

84 Set register constants with build traceability information. 

85 """ 

86 # Crude way of finding the module that the top-level entity belongs to. 

87 top_module = self.modules.get(module_name=self.top.split("_top")[0]) 

88 registers: RegisterList = top_module.registers 

89 

90 hook_note = """ 

91 

92Note that this constant is added by a Python build hook. 

93It is available in the FPGA build flow and the generated software code, 

94but never in the simulation flow. 

95""" 

96 

97 registers.add_constant( 

98 name="expected_build_id", 

99 value=generics["build_id"], 

100 description="""\ 

101The build ID for this FPGA build. 

102The value read from the **build_id** register shall be equal to this constant.""" 

103 + hook_note, 

104 ) 

105 

106 registers.add_constant( 

107 name="build_project_name", 

108 value=self.name, 

109 description=( 

110 "The name of the build project that executed the build of this FPGA." + hook_note 

111 ), 

112 ) 

113 

114 registers.add_constant( 

115 name="build_generics", 

116 value=", ".join(f"{key}={value}" for key, value in generics.items()), 

117 description=( 

118 "The generic values that were set to the top level when building this FPGA." 

119 + hook_note 

120 ), 

121 ) 

122 

123 registers.add_constant( 

124 name="build_vivado_version", 

125 value=get_vivado_version(vivado_path=self._vivado_path), 

126 description=("The Vivado version that this FPGA was built with." + hook_note), 

127 ) 

128 

129 registers.add_constant( 

130 name="build_git_commit", 

131 value=get_git_commit(directory=REPO_ROOT), 

132 description="""\ 

133The left-most characters of the git commit hash that this FPGA was built from. 

134If there were local changes in the git repository, this will be noted in parenthesis. 

135""" 

136 + hook_note, 

137 ) 

138 

139 registers.add_constant( 

140 name="build_time", 

141 # Specific timezone is not needed, it just needs to be consistent. 

142 value=( 

143 datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") # noqa: DTZ005 

144 ), 

145 description="A string describing at what time and date the FPGA was built." + hook_note, 

146 ) 

147 

148 registers.add_constant( 

149 name="build_hostname", 

150 value=platform.node(), 

151 description="The hostname where this FPGA was built." + hook_note, 

152 ) 

153 

154 registers.add_constant( 

155 name="build_operating_system", 

156 value=platform.system(), 

157 description="The operating that this FPGA was built on." + hook_note, 

158 ) 

159 

160 registers.add_constant( 

161 name="build_operating_system_info", 

162 value=platform.version() + " " + platform.platform(), 

163 description="More information about the operating system that this FPGA was built on." 

164 + hook_note, 

165 ) 

166 

167 

168class TsfpgaExampleVivadoNetlistProject(VivadoNetlistProject): 

169 """ 

170 Example Vivado project class for netlist builds. 

171 Shows how to override and extend the base behavior. 

172 """ 

173 

174 def pre_create( 

175 self, 

176 generics: dict[str, Any], 

177 **kwargs: Any, # noqa: ANN401 

178 ) -> bool: 

179 """ 

180 Override parent method to add custom behavior. 

181 Update TCL sources just before project creation. 

182 """ 

183 self.tcl_sources.append(THIS_DIR / "tcl" / "example_vivado_messages.tcl") 

184 self.tcl_sources.append(THIS_DIR / "tcl" / "example_vivado_netlist_messages.tcl") 

185 

186 return super().pre_create(generics=generics, kwargs=kwargs)