Coverage for apio/scons/plugin_xilinx.py: 100%

71 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-06-24 03:51 +0000

1# -*- coding: utf-8 -*- 

2# -- This file is part of the Apio project 

3# -- (C) 2016-2018 FPGAwars 

4# -- Author Jesús Arroyo 

5# -- License GPLv2 

6# -- Derived from: 

7# ---- Platformio project 

8# ---- (C) 2014-2016 Ivan Kravets <me@ikravets.com> 

9# ---- License Apache v2 

10 

11"""Apio scons plugin for the xilinx architecture.""" 

12 

13# pylint: disable=duplicate-code 

14 

15from pathlib import Path 

16from SCons.Script import Builder 

17from SCons.Builder import BuilderBase, CompositeBuilder 

18from apio.common.common_util import SRC_SUFFIXES 

19from apio.scons.apio_env import ApioEnv 

20from apio.scons.plugin_base import PluginBase, ArchPluginInfo 

21from apio.scons.plugin_util import ( 

22 verilator_lint_action, 

23 has_testbench_name, 

24 announce_testbench_action, 

25 source_files_issue_scanner_action, 

26 iverilog_action, 

27 basename, 

28 make_verilator_config_builder, 

29 get_define_flags, 

30) 

31 

32 

33class PluginXilinx(PluginBase): 

34 """Apio scons plugin for the Xilinx architecture.""" 

35 

36 def __init__(self, apio_env: ApioEnv): 

37 # -- Call parent constructor. 

38 super().__init__(apio_env) 

39 

40 # -- Cache values. 

41 yosys_path = Path(apio_env.params.environment.yosys_path) 

42 self.yosys_lib_dir = yosys_path / "xilinx" 

43 self.sim_lib_files = [yosys_path / "xilinx" / "cells_sim.v"] 

44 self.lint_lib_files = self.sim_lib_files 

45 

46 def plugin_info(self) -> ArchPluginInfo: 

47 """Return plugin specific parameters.""" 

48 return ArchPluginInfo( 

49 constrains_file_suffix=".xdc", 

50 pnr_file_suffix=".frames", 

51 bitstream_file_suffix=".bit", 

52 clk_name_index=0, 

53 ) 

54 

55 # @overrides 

56 def synth_builder(self) -> BuilderBase | CompositeBuilder: 

57 """Creates and returns the synth builder.""" 

58 

59 # -- Keep short references. 

60 apio_env = self.apio_env 

61 params = apio_env.params 

62 xilinx_params = params.fpga_info.xilinx_params 

63 

64 # -- The yosys synth builder. 

65 return Builder( 

66 action=( 

67 'yosys -p "synth_xilinx -arch {0} -top {1}; ' 

68 'write_json $TARGET {2} " ' 

69 "{3} -DSYNTHESIZE {4} $SOURCES" 

70 ).format( 

71 xilinx_params.yosys_arch, 

72 params.apio_env_params.top_module, 

73 " ".join(params.apio_env_params.yosys_extra_options), 

74 "" if params.verbosity.all or params.verbosity.synth else "-q", 

75 get_define_flags(apio_env), 

76 ), 

77 suffix=".json", 

78 src_suffix=SRC_SUFFIXES, 

79 source_scanner=self.verilog_src_scanner, 

80 ) 

81 

82 # @overrides 

83 def pnr_builder(self) -> BuilderBase | CompositeBuilder: 

84 """Creates and returns the pnr builder.""" 

85 

86 # -- Keep short references. 

87 apio_env = self.apio_env 

88 params = apio_env.params 

89 xilinx_params = params.fpga_info.xilinx_params 

90 

91 # -- We use an emmiter to add to the builder a second output file. 

92 def emitter(target, source, env): 

93 _ = env # Unused 

94 target.append(apio_env.target + ".pnr") 

95 return target, source 

96 

97 package = xilinx_params.package 

98 database = f"{package}.bin" 

99 chipdb = Path(apio_env.params.environment.chipdb_path) 

100 

101 # -- Python file that is passed to nextpnr-xilinx for generating 

102 # -- the report file 

103 

104 # -- Get the full path of this file (plugin_xilinx.py) 

105 current_python_file = Path(__file__) 

106 

107 # -- The parent folder is the apio root folder 

108 apio_root = current_python_file.parent.parent 

109 

110 # -- Add the report_xilinx.py folder to the path 

111 report_py = apio_root / "scons/report_xilinx.py" 

112 

113 # -- Create the builder 

114 return Builder( 

115 action=( 

116 "nextpnr-xilinx --chipdb {0} --xdc {1} --json $SOURCE " 

117 "--fasm $TARGET " 

118 "--post-route {2} " 

119 "-q " 

120 ).format(chipdb / database, self.constrain_file(), report_py), 

121 suffix=".fasm", 

122 src_suffix=".json", 

123 emitter=emitter, 

124 ) 

125 

126 # @overrides 

127 def bitstream_pre_builder(self) -> BuilderBase | CompositeBuilder: 

128 """Creates and returns the pre-bitstream builder.""" 

129 

130 # -- Keep short references. 

131 apio_env = self.apio_env 

132 params = apio_env.params 

133 xilinx_params = params.fpga_info.xilinx_params 

134 

135 part1 = f"{xilinx_params.package}-{xilinx_params.speed}" 

136 prjxray_db = Path(apio_env.params.environment.prjxray_db_path) 

137 prjxray_db = prjxray_db / xilinx_params.family 

138 

139 return Builder( 

140 action="fasm2frames --part {0} --db-root {1} " 

141 " $SOURCE > $TARGET ".format( 

142 part1, 

143 prjxray_db, 

144 ), 

145 suffix=".frames", 

146 src_suffix=".fasm", 

147 ) 

148 

149 # @overrides 

150 def bitstream_builder(self) -> BuilderBase | CompositeBuilder: 

151 """Creates and returns the bitstream builder.""" 

152 

153 # -- Keep short references. 

154 apio_env = self.apio_env 

155 params = apio_env.params 

156 xilinx_params = params.fpga_info.xilinx_params 

157 part1 = f"{xilinx_params.package}-{xilinx_params.speed}" 

158 

159 prjxray_db = Path(apio_env.params.environment.prjxray_db_path) 

160 prjxray_db = prjxray_db / xilinx_params.family 

161 part_file = prjxray_db / part1 / "part.yaml" 

162 

163 return Builder( 

164 action="xc7frames2bit --part_file {0} --part_name {1} " 

165 "--frm_file " 

166 "$SOURCE --output_file $TARGET".format( 

167 part_file, 

168 part1, 

169 ), 

170 suffix=".bit", 

171 src_suffix=".frames", 

172 ) 

173 

174 # @overrides 

175 def testbench_compile_builder(self) -> BuilderBase | CompositeBuilder: 

176 """Creates and returns the testbench compile builder.""" 

177 

178 # -- Keep short references. 

179 apio_env = self.apio_env 

180 params = apio_env.params 

181 

182 # -- Sanity checks 

183 assert apio_env.targeting_one_of("sim", "test") 

184 assert params.target.HasField("sim") or params.target.HasField("test") 

185 

186 # -- We use a generator because we need a different action 

187 # -- string for sim and test. 

188 def action_generator(target, source, env, for_signature): 

189 _ = (source, env, for_signature) # Unused 

190 # Extract testbench name from target file name. 

191 testbench_file = str(target[0]) 

192 assert has_testbench_name(testbench_file), testbench_file 

193 testbench_name = basename(testbench_file) 

194 

195 # Construct the actions list. 

196 action = [ 

197 # -- Print a testbench title. 

198 announce_testbench_action(), 

199 # -- Scan source files for issues. 

200 source_files_issue_scanner_action(), 

201 # -- Perform the actual test or sim compilation. 

202 iverilog_action( 

203 apio_env, 

204 verbose=params.verbosity.all, 

205 vcd_output_name=testbench_name, 

206 is_interactive=apio_env.targeting_one_of("sim"), 

207 lib_dirs=[self.yosys_lib_dir], 

208 lib_files=self.sim_lib_files, 

209 ), 

210 ] 

211 return action 

212 

213 # -- The testbench compiler builder. 

214 return Builder( 

215 # -- Dynamic action string generator. 

216 generator=action_generator, 

217 suffix=".out", 

218 src_suffix=SRC_SUFFIXES, 

219 source_scanner=self.verilog_src_scanner, 

220 ) 

221 

222 # @overrides 

223 def lint_config_builder(self) -> BuilderBase: 

224 """Creates and returns the lint config builder.""" 

225 

226 # -- Sanity checks 

227 assert self.apio_env.targeting_one_of("lint") 

228 

229 # -- Make the builder. 

230 return make_verilator_config_builder( 

231 self.yosys_lib_dir, 

232 rules_to_supress=[ 

233 "SPECIFYIGN", 

234 ], 

235 ) 

236 

237 # @overrides 

238 def lint_builder(self) -> BuilderBase | CompositeBuilder: 

239 """Creates and returns the lint builder.""" 

240 

241 return Builder( 

242 action=verilator_lint_action( 

243 self.apio_env, 

244 lib_dirs=[self.yosys_lib_dir], 

245 lib_files=self.lint_lib_files, 

246 ), 

247 src_suffix=SRC_SUFFIXES, 

248 source_scanner=self.verilog_src_scanner, 

249 )