Coverage for apio/scons/plugin_xilinx.py: 100%
56 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03:53 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03:53 +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 make_verilator_config_builder,
28 get_define_flags,
29)
32class PluginXilinx(PluginBase):
33 """Apio scons plugin for the Xilinx architecture."""
35 def __init__(self, apio_env: ApioEnv):
36 # -- Call parent constructor.
37 super().__init__(apio_env)
39 # -- Cache values.
40 yosys_path = Path(apio_env.params.environment.yosys_path)
41 self.yosys_lib_dir = yosys_path / "xilinx"
42 self.sim_lib_files = [yosys_path / "xilinx" / "cells_sim.v"]
43 # -- For lint, also pass the black-box declarations of the primitives
44 # -- that have no simulation model in cells_sim.v (PLLE2_*, MMCME2_*,
45 # -- etc.); without them verilator fails with MODMISSING on any design
46 # -- that instantiates one. The two files declare disjoint modules.
47 self.lint_lib_files = self.sim_lib_files + [
48 yosys_path / "xilinx" / "cells_xtra.v"
49 ]
51 def plugin_info(self) -> ArchPluginInfo:
52 """Return plugin specific parameters."""
53 return ArchPluginInfo(
54 constrains_file_suffix=".xdc",
55 pnr_file_suffix=".frames",
56 bitstream_file_suffix=".bit",
57 )
59 # @overrides
60 def make_synth_builder(self) -> BuilderBase | CompositeBuilder:
61 """Creates and returns the synth builder."""
63 # -- Keep short references.
64 apio_env = self.apio_env
65 params = apio_env.params
66 xilinx_params = params.fpga_info.xilinx_params
68 # -- The yosys synth builder.
69 return Builder(
70 action=(
71 # -- yosys-extra-options goes INSIDE the synth_xilinx command
72 # -- (like the other architectures do with synth_ice40/ecp5/
73 # -- gowin), so synth flags such as -nodsp work; it used to
74 # -- land after write_json, where it did nothing useful.
75 'yosys -p "synth_xilinx -arch {0} -top {1} {2}; '
76 'write_json $TARGET " '
77 "{3} -DSYNTHESIZE {4} $SOURCES"
78 ).format(
79 xilinx_params.yosys_arch,
80 params.apio_env_params.top_module,
81 " ".join(params.apio_env_params.yosys_extra_options),
82 "" if params.verbosity.all or params.verbosity.synth else "-q",
83 get_define_flags(apio_env),
84 ),
85 suffix=".json",
86 source_scanner=self.verilog_src_scanner,
87 src_suffix=SRC_SUFFIXES,
88 )
90 # @overrides
91 def make_pnr_builder(self) -> BuilderBase | CompositeBuilder:
92 """Creates and returns the pnr builder."""
94 # -- Keep short references.
95 apio_env = self.apio_env
96 params = apio_env.params
97 xilinx_params = params.fpga_info.xilinx_params
99 # -- We use an emmiter to add to the builder a second output file.
100 def emitter(target, source, env):
101 _ = env # Unused
102 target.append(apio_env.target + ".pnr")
103 return target, source
105 # -- Create the builder
106 return Builder(
107 action=(
108 "nextpnr-xilinx --chipdb {0} --xdc {1} --json $SOURCE "
109 "--fasm $TARGET --report {2} {3} {4}"
110 ).format(
111 xilinx_params.chipdb_file_path,
112 self.constrain_file(),
113 apio_env.target + ".pnr",
114 ("" if params.verbosity.all or params.verbosity.pnr else "-q"),
115 " ".join(params.apio_env_params.nextpnr_extra_options),
116 ),
117 src_suffix=".json",
118 suffix=".fasm",
119 emitter=emitter,
120 )
122 # @overrides
123 def make_bitstream_builder(self) -> BuilderBase | CompositeBuilder:
124 """Creates and returns the bitstream builder."""
126 # -- Keep short references.
127 apio_env = self.apio_env
128 params = apio_env.params
129 xilinx_params = params.fpga_info.xilinx_params
131 prjxray_db = Path(apio_env.params.environment.xilinx_prjxray_db_path)
132 prjxray_db = prjxray_db / xilinx_params.yosys_family
133 part_file = prjxray_db / xilinx_params.yosys_part / "part.yaml"
135 # -- Intermediate .frames file path expression. When resolved, it's
136 # -- the same as target file but with the ".frames" extension.
137 frames_file_macro = "${TARGET.base}.frames"
139 return Builder(
140 action=[
141 # -- STEP1: Converts .fasm to .frames
142 "fasm2frames --part {0} --db-root {1} "
143 " $SOURCE > {2} ".format(
144 xilinx_params.yosys_part, prjxray_db, frames_file_macro
145 ),
146 # -- STEP2 Converts .frames to .bit
147 "xc7frames2bit --part_file {0} --part_name {1} "
148 "--frm_file {2} --output_file $TARGET".format(
149 part_file, xilinx_params.yosys_part, frames_file_macro
150 ),
151 ],
152 src_suffix=".fasm",
153 suffix=".bit",
154 )
156 # @overrides
157 def make_testbench_compile_builder(self) -> BuilderBase | CompositeBuilder:
158 """Creates and returns the testbench compile builder."""
160 # -- Keep short references.
161 apio_env = self.apio_env
162 params = apio_env.params
164 # -- Sanity checks
165 assert apio_env.targeting_one_of("sim", "test")
166 assert params.target.HasField("sim") or params.target.HasField("test")
168 # -- We use a generator because we need a different action
169 # -- string for sim and test.
170 def action_generator(target, source, env, for_signature):
171 _ = (source, env, for_signature) # Unused
172 # Extract testbench file name from the target.
173 testbench_file = str(target[0])
174 assert has_testbench_name(testbench_file), testbench_file
176 # Construct the actions list.
177 action = [
178 # -- Print a testbench title.
179 announce_testbench_action(),
180 # -- Scan source files for issues.
181 source_files_issue_scanner_action(),
182 # -- Perform the actual test or sim compilation.
183 iverilog_action(
184 apio_env,
185 verbose=params.verbosity.all,
186 is_interactive=apio_env.targeting_one_of("sim"),
187 lib_dirs=[self.yosys_lib_dir],
188 lib_files=self.sim_lib_files,
189 ),
190 ]
191 return action
193 # -- The testbench compiler builder.
194 return Builder(
195 # -- Dynamic action string generator.
196 generator=action_generator,
197 source_scanner=self.verilog_src_scanner,
198 src_suffix=SRC_SUFFIXES,
199 suffix=".out",
200 )
202 # @overrides
203 def make_lint_config_builder(self) -> BuilderBase:
204 """Creates and returns the lint config builder."""
206 # -- Sanity checks
207 assert self.apio_env.targeting_one_of("lint")
209 # -- Make the builder.
210 # -- See https://verilator.org/guide/latest/warnings.html
211 return make_verilator_config_builder(
212 self.yosys_lib_dir,
213 rules_to_suppress=[
214 "SPECIFYIGN",
215 ],
216 )
218 # @overrides
219 def make_lint_builder(self) -> BuilderBase | CompositeBuilder:
220 """Creates and returns the lint builder."""
222 return Builder(
223 action=verilator_lint_action(
224 self.apio_env,
225 lib_dirs=[self.yosys_lib_dir],
226 lib_files=self.lint_lib_files,
227 ),
228 source_scanner=self.verilog_src_scanner,
229 src_suffix=SRC_SUFFIXES,
230 )