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
« 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
11"""Apio scons plugin for the xilinx architecture."""
13# pylint: disable=duplicate-code
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)
33class PluginXilinx(PluginBase):
34 """Apio scons plugin for the Xilinx architecture."""
36 def __init__(self, apio_env: ApioEnv):
37 # -- Call parent constructor.
38 super().__init__(apio_env)
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
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 )
55 # @overrides
56 def synth_builder(self) -> BuilderBase | CompositeBuilder:
57 """Creates and returns the synth builder."""
59 # -- Keep short references.
60 apio_env = self.apio_env
61 params = apio_env.params
62 xilinx_params = params.fpga_info.xilinx_params
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 )
82 # @overrides
83 def pnr_builder(self) -> BuilderBase | CompositeBuilder:
84 """Creates and returns the pnr builder."""
86 # -- Keep short references.
87 apio_env = self.apio_env
88 params = apio_env.params
89 xilinx_params = params.fpga_info.xilinx_params
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
97 package = xilinx_params.package
98 database = f"{package}.bin"
99 chipdb = Path(apio_env.params.environment.chipdb_path)
101 # -- Python file that is passed to nextpnr-xilinx for generating
102 # -- the report file
104 # -- Get the full path of this file (plugin_xilinx.py)
105 current_python_file = Path(__file__)
107 # -- The parent folder is the apio root folder
108 apio_root = current_python_file.parent.parent
110 # -- Add the report_xilinx.py folder to the path
111 report_py = apio_root / "scons/report_xilinx.py"
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 )
126 # @overrides
127 def bitstream_pre_builder(self) -> BuilderBase | CompositeBuilder:
128 """Creates and returns the pre-bitstream builder."""
130 # -- Keep short references.
131 apio_env = self.apio_env
132 params = apio_env.params
133 xilinx_params = params.fpga_info.xilinx_params
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
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 )
149 # @overrides
150 def bitstream_builder(self) -> BuilderBase | CompositeBuilder:
151 """Creates and returns the bitstream builder."""
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}"
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"
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 )
174 # @overrides
175 def testbench_compile_builder(self) -> BuilderBase | CompositeBuilder:
176 """Creates and returns the testbench compile builder."""
178 # -- Keep short references.
179 apio_env = self.apio_env
180 params = apio_env.params
182 # -- Sanity checks
183 assert apio_env.targeting_one_of("sim", "test")
184 assert params.target.HasField("sim") or params.target.HasField("test")
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)
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
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 )
222 # @overrides
223 def lint_config_builder(self) -> BuilderBase:
224 """Creates and returns the lint config builder."""
226 # -- Sanity checks
227 assert self.apio_env.targeting_one_of("lint")
229 # -- Make the builder.
230 return make_verilator_config_builder(
231 self.yosys_lib_dir,
232 rules_to_supress=[
233 "SPECIFYIGN",
234 ],
235 )
237 # @overrides
238 def lint_builder(self) -> BuilderBase | CompositeBuilder:
239 """Creates and returns the lint builder."""
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 )