Coverage for apio / scons / plugin_ecp5.py: 100%
50 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 02:31 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 02:31 +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
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 PluginEcp5(PluginBase):
34 """Apio scons plugin for the ice40 architecture."""
36 def __init__(self, apio_env: ApioEnv):
37 # -- Call parent constructor.
38 super().__init__(apio_env)
40 # -- Cache values.
41 trellis_path = Path(apio_env.params.environment.trellis_path)
42 yosys_path = Path(apio_env.params.environment.yosys_path)
44 self.database_path = trellis_path / "database"
45 self.yosys_lib_dir = yosys_path / "ecp5"
46 self.sim_lib_files = [yosys_path / "ecp5" / "cells_sim.v"]
47 # -- For black-box cells such as EHXPLLL PLL.
48 self.lint_lib_files = [yosys_path / "ecp5" / "cells_bb.v"]
50 def plugin_info(self) -> ArchPluginInfo:
51 """Return plugin specific parameters."""
52 return ArchPluginInfo(
53 constrains_file_suffix=".lpf",
54 pnr_file_suffix=".config",
55 bitstream_file_suffix=".bit",
56 clk_name_index=2,
57 )
59 # @overrides
60 def synth_builder(self) -> BuilderBase:
61 """Creates and returns the synth builder."""
63 # -- Keep short references.
64 apio_env = self.apio_env
65 params = apio_env.params
67 # -- The yosys synth builder.
68 return Builder(
69 action=(
70 'yosys -p "synth_ecp5 -top {0} -json $TARGET {1}" '
71 "{2} -DSYNTHESIZE {3} $SOURCES"
72 ).format(
73 params.apio_env_params.top_module,
74 " ".join(params.apio_env_params.yosys_extra_options),
75 "" if params.verbosity.all or params.verbosity.synth else "-q",
76 get_define_flags(apio_env),
77 ),
78 suffix=".json",
79 src_suffix=SRC_SUFFIXES,
80 source_scanner=self.verilog_src_scanner,
81 )
83 # @overrides
84 def pnr_builder(self) -> BuilderBase:
85 """Creates and returns the pnr builder."""
87 # -- Keep short references.
88 apio_env = self.apio_env
89 params = apio_env.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 # -- Create the builder.
98 return Builder(
99 action=(
100 "nextpnr-ecp5 --{0} --package {1} --speed {2} "
101 "--json $SOURCE --textcfg $TARGET "
102 "--report {3} --lpf {4} --timing-allow-fail --force "
103 "{5} {6} {7}"
104 ).format(
105 params.fpga_info.ecp5_params.type,
106 params.fpga_info.ecp5_params.package,
107 params.fpga_info.ecp5_params.speed,
108 apio_env.target + ".pnr",
109 self.constrain_file(),
110 "" if params.verbosity.all or params.verbosity.pnr else "-q",
111 "--gui" if params.nextpnr_gui else "",
112 " ".join(params.apio_env_params.nextpnr_extra_options),
113 ),
114 suffix=".config",
115 src_suffix=".json",
116 emitter=emitter,
117 )
119 # @overrides
120 def bitstream_builder(self) -> BuilderBase:
121 """Creates and returns the bitstream builder."""
123 return Builder(
124 action="ecppack --compress --db {0} $SOURCE $TARGET".format(
125 self.database_path,
126 ),
127 suffix=".bit",
128 src_suffix=".config",
129 )
131 # @overrides
132 def testbench_compile_builder(self) -> BuilderBase:
133 """Creates and returns the testbench compile builder."""
134 # -- Keep short references.
135 apio_env = self.apio_env
136 params = apio_env.params
138 # -- Sanity checks
139 assert apio_env.targeting_one_of("sim", "test")
140 assert params.target.HasField("sim") or params.target.HasField("test")
142 # -- We use a generator because we need a different action
143 # -- string for sim and test.
144 def action_generator(target, source, env, for_signature):
145 _ = (source, env, for_signature) # Unused
146 # Extract testbench name from target file name.
147 testbench_file = str(target[0])
148 assert has_testbench_name(testbench_file), testbench_file
149 testbench_name = basename(testbench_file)
151 # Construct the actions list.
152 action = [
153 # -- Print a testbench title.
154 announce_testbench_action(),
155 # -- Scan source files for issues.
156 source_files_issue_scanner_action(),
157 # -- Perform the actual test or sim compilation.
158 iverilog_action(
159 apio_env,
160 verbose=params.verbosity.all,
161 vcd_output_name=testbench_name,
162 is_interactive=apio_env.targeting_one_of("sim"),
163 # -- Per https://github.com/YosysHQ/yosys/issues/5668
164 extra_params=["-DNO_INCLUDES"],
165 lib_dirs=[self.yosys_lib_dir],
166 lib_files=self.sim_lib_files,
167 ),
168 ]
169 return action
171 # -- The testbench compiler builder.
172 return Builder(
173 # -- Dynamic action string generator.
174 generator=action_generator,
175 suffix=".out",
176 src_suffix=SRC_SUFFIXES,
177 source_scanner=self.verilog_src_scanner,
178 )
180 # @overrides
181 def lint_config_builder(self) -> BuilderBase:
182 """Creates and returns the lint config builder."""
184 # -- Sanity checks
185 assert self.apio_env.targeting_one_of("lint")
187 # -- Make the builder.
188 return make_verilator_config_builder(
189 self.yosys_lib_dir,
190 rules_to_supress=[],
191 )
193 # @overrides
194 def lint_builder(self) -> BuilderBase:
195 """Creates and returns the lint builder."""
197 return Builder(
198 action=verilator_lint_action(
199 self.apio_env,
200 lib_dirs=[self.yosys_lib_dir],
201 lib_files=self.lint_lib_files,
202 ),
203 src_suffix=SRC_SUFFIXES,
204 source_scanner=self.verilog_src_scanner,
205 )