Coverage for apio/scons/plugin_ecp5.py: 100%
49 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 ecp5 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 PluginEcp5(PluginBase):
33 """Apio scons plugin for the ice40 architecture."""
35 def __init__(self, apio_env: ApioEnv):
36 # -- Call parent constructor.
37 super().__init__(apio_env)
39 # -- Cache values.
40 trellis_path = Path(apio_env.params.environment.trellis_path)
41 yosys_path = Path(apio_env.params.environment.yosys_path)
43 self.database_path = trellis_path / "database"
44 self.yosys_lib_dir = yosys_path / "ecp5"
45 self.sim_lib_files = [yosys_path / "ecp5" / "cells_sim.v"]
46 # -- For lint, the simulation models PLUS the black-box cells that
47 # -- have no model in cells_sim.v (EHXPLLL, DCUA, ...). Passing only
48 # -- cells_bb.v (as before) made verilator fail with MODMISSING on
49 # -- designs that instantiate simulation-modeled cells (IO cells,
50 # -- DP16KD, ...). The two files declare disjoint modules.
51 self.lint_lib_files = self.sim_lib_files + [
52 yosys_path / "ecp5" / "cells_bb.v"
53 ]
55 def plugin_info(self) -> ArchPluginInfo:
56 """Return plugin specific parameters."""
57 return ArchPluginInfo(
58 constrains_file_suffix=".lpf",
59 pnr_file_suffix=".config",
60 bitstream_file_suffix=".bit",
61 )
63 # @overrides
64 def make_synth_builder(self) -> BuilderBase | CompositeBuilder:
65 """Creates and returns the synth builder."""
67 # -- Keep short references.
68 apio_env = self.apio_env
69 params = apio_env.params
71 # -- The yosys synth builder.
72 return Builder(
73 action=(
74 'yosys -p "synth_ecp5 -top {0} -json $TARGET {1}" '
75 "{2} -DSYNTHESIZE {3} $SOURCES"
76 ).format(
77 params.apio_env_params.top_module,
78 " ".join(params.apio_env_params.yosys_extra_options),
79 "" if params.verbosity.all or params.verbosity.synth else "-q",
80 get_define_flags(apio_env),
81 ),
82 source_scanner=self.verilog_src_scanner,
83 src_suffix=SRC_SUFFIXES,
84 suffix=".json",
85 )
87 # @overrides
88 def make_pnr_builder(self) -> BuilderBase | CompositeBuilder:
89 """Creates and returns the pnr builder."""
91 # -- Keep short references.
92 apio_env = self.apio_env
93 params = apio_env.params
95 # -- We use an emmiter to add to the builder a second output file.
96 def emitter(target, source, env):
97 _ = env # Unused
98 target.append(apio_env.target + ".pnr")
99 return target, source
101 # -- Create the builder.
102 return Builder(
103 action=(
104 "nextpnr-ecp5 --{0} --package {1} --speed {2} "
105 "--json $SOURCE --textcfg $TARGET "
106 "--report {3} --lpf {4} --timing-allow-fail --force "
107 "{5} {6}"
108 ).format(
109 params.fpga_info.ecp5_params.type,
110 params.fpga_info.ecp5_params.package,
111 params.fpga_info.ecp5_params.speed,
112 apio_env.target + ".pnr",
113 self.constrain_file(),
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=".config",
119 emitter=emitter,
120 )
122 # @overrides
123 def make_bitstream_builder(self) -> BuilderBase | CompositeBuilder:
124 """Creates and returns the bitstream builder."""
126 return Builder(
127 action="ecppack --compress --db {0} $SOURCE $TARGET".format(
128 self.database_path,
129 ),
130 src_suffix=".config",
131 suffix=".bit",
132 )
134 # @overrides
135 def make_testbench_compile_builder(self) -> BuilderBase | CompositeBuilder:
136 """Creates and returns the testbench compile builder."""
137 # -- Keep short references.
138 apio_env = self.apio_env
139 params = apio_env.params
141 # -- Sanity checks
142 assert apio_env.targeting_one_of("sim", "test")
143 assert params.target.HasField("sim") or params.target.HasField("test")
145 # -- We use a generator because we need a different action
146 # -- string for sim and test.
147 def action_generator(target, source, env, for_signature):
148 _ = (source, env, for_signature) # Unused
149 # Extract testbench file name from the target.
150 testbench_file = str(target[0])
151 assert has_testbench_name(testbench_file), testbench_file
153 # Construct the actions list.
154 action = [
155 # -- Print a testbench title.
156 announce_testbench_action(),
157 # -- Scan source files for issues.
158 source_files_issue_scanner_action(),
159 # -- Perform the actual test or sim compilation.
160 iverilog_action(
161 apio_env,
162 verbose=params.verbosity.all,
163 is_interactive=apio_env.targeting_one_of("sim"),
164 # -- Per https://github.com/YosysHQ/yosys/issues/5668
165 extra_params=["-DNO_INCLUDES"],
166 lib_dirs=[self.yosys_lib_dir],
167 lib_files=self.sim_lib_files,
168 ),
169 ]
170 return action
172 # -- The testbench compiler builder.
173 return Builder(
174 # -- Dynamic action string generator.
175 generator=action_generator,
176 source_scanner=self.verilog_src_scanner,
177 src_suffix=SRC_SUFFIXES,
178 suffix=".out",
179 )
181 # @overrides
182 def make_lint_config_builder(self) -> BuilderBase:
183 """Creates and returns the lint config builder."""
185 # -- Sanity checks
186 assert self.apio_env.targeting_one_of("lint")
188 # -- Make the builder.
189 # -- See https://verilator.org/guide/latest/warnings.html
190 return make_verilator_config_builder(
191 self.yosys_lib_dir,
192 rules_to_suppress=[
193 # -- cells_sim.v double-includes cells_ff.vh/cells_io.vh
194 # -- (directly and via common_sim.vh); the duplicates are
195 # -- identical and verilator keeps the first definition.
196 "MODDUP",
197 # -- Benign width mismatch in common_sim.vh (BB model).
198 "WIDTHEXPAND",
199 # -- The techmap wrappers in cells_io.vh/cells_ff.vh
200 # -- instantiate TRELLIS_* cells with partial pin lists on
201 # -- purpose; user instances keep full pin checking (the
202 # -- waiver is scoped to the yosys lib dir).
203 "PINMISSING",
204 ],
205 )
207 # @overrides
208 def make_lint_builder(self) -> BuilderBase | CompositeBuilder:
209 """Creates and returns the lint builder."""
211 return Builder(
212 action=verilator_lint_action(
213 self.apio_env,
214 lib_dirs=[self.yosys_lib_dir],
215 lib_files=self.lint_lib_files,
216 ),
217 source_scanner=self.verilog_src_scanner,
218 src_suffix=SRC_SUFFIXES,
219 )