Coverage for apio / scons / plugin_ice40.py: 100%
48 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 ice40 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 PluginIce40(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 yosys_path = Path(apio_env.params.environment.yosys_path)
42 self.yosys_lib_dir = yosys_path / "ice40"
43 self.sim_lib_files = [yosys_path / "ice40" / "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=".pcf",
50 pnr_file_suffix=".asc",
51 bitstream_file_suffix=".bin",
52 clk_name_index=0,
53 )
55 # @overrides
56 def synth_builder(self) -> BuilderBase:
57 """Creates and returns the synth builder."""
59 # -- Keep short references.
60 apio_env = self.apio_env
61 params = apio_env.params
63 # -- The yosys synth builder.
64 return Builder(
65 action=(
66 'yosys -p "synth_ice40 -top {0} -json $TARGET {1}" '
67 "{2} -DSYNTHESIZE {3} $SOURCES"
68 ).format(
69 params.apio_env_params.top_module,
70 " ".join(params.apio_env_params.yosys_extra_options),
71 "" if params.verbosity.all or params.verbosity.synth else "-q",
72 get_define_flags(apio_env),
73 ),
74 suffix=".json",
75 src_suffix=SRC_SUFFIXES,
76 source_scanner=self.verilog_src_scanner,
77 )
79 # @overrides
80 def pnr_builder(self) -> BuilderBase:
81 """Creates and returns the pnr builder."""
83 # -- Keep short references.
84 apio_env = self.apio_env
85 params = apio_env.params
87 # -- We use an emmiter to add to the builder a second output file.
88 def emitter(target, source, env):
89 _ = env # Unused
90 target.append(apio_env.target + ".pnr")
91 return target, source
93 # -- Create the builder.
94 return Builder(
95 action=(
96 "nextpnr-ice40 --{0} --package {1} --json $SOURCE "
97 "--asc $TARGET --report {2} --pcf {3} {4} {5} {6}"
98 ).format(
99 params.fpga_info.ice40_params.type,
100 params.fpga_info.ice40_params.package,
101 apio_env.target + ".pnr",
102 self.constrain_file(),
103 "" if params.verbosity.all or params.verbosity.pnr else "-q",
104 "--gui" if params.nextpnr_gui else "",
105 " ".join(params.apio_env_params.nextpnr_extra_options),
106 ),
107 suffix=".asc",
108 src_suffix=".json",
109 emitter=emitter,
110 )
112 # @overrides
113 def bitstream_builder(self) -> BuilderBase:
114 """Creates and returns the bitstream builder."""
115 return Builder(
116 action="icepack $SOURCE $TARGET",
117 suffix=".bin",
118 src_suffix=".asc",
119 )
121 # @overrides
122 def testbench_compile_builder(self) -> BuilderBase:
123 """Creates and returns the testbench compile builder."""
125 # -- Keep short references.
126 apio_env = self.apio_env
127 params = apio_env.params
129 # -- Sanity checks
130 assert apio_env.targeting_one_of("sim", "test")
131 assert params.target.HasField("sim") or params.target.HasField("test")
133 # -- We use a generator because we need a different action
134 # -- string for sim and test.
135 def action_generator(target, source, env, for_signature):
136 _ = (source, env, for_signature) # Unused
137 # Extract testbench name from target file name.
138 testbench_file = str(target[0])
139 assert has_testbench_name(testbench_file), testbench_file
140 testbench_name = basename(testbench_file)
142 # Construct the actions list.
143 action = [
144 # -- Print a testbench title.
145 announce_testbench_action(),
146 # -- Scan source files for issues.
147 source_files_issue_scanner_action(),
148 # -- Perform the actual test or sim compilation.
149 iverilog_action(
150 apio_env,
151 verbose=params.verbosity.all,
152 vcd_output_name=testbench_name,
153 is_interactive=apio_env.targeting_one_of("sim"),
154 extra_params=["-DNO_ICE40_DEFAULT_ASSIGNMENTS"],
155 lib_dirs=[self.yosys_lib_dir],
156 lib_files=self.sim_lib_files,
157 ),
158 ]
159 return action
161 # -- The testbench compiler builder.
162 return Builder(
163 # -- Dynamic action string generator.
164 generator=action_generator,
165 suffix=".out",
166 src_suffix=SRC_SUFFIXES,
167 source_scanner=self.verilog_src_scanner,
168 )
170 # @overrides
171 def lint_config_builder(self) -> BuilderBase:
172 """Creates and returns the lint config builder."""
174 # -- Sanity checks
175 assert self.apio_env.targeting_one_of("lint")
177 # -- Make the builder.
178 return make_verilator_config_builder(
179 self.yosys_lib_dir,
180 rules_to_supress=[],
181 )
183 # @overrides
184 def lint_builder(self) -> BuilderBase:
185 """Creates and returns the lint builder."""
187 return Builder(
188 action=verilator_lint_action(
189 self.apio_env,
190 extra_params=["-DNO_ICE40_DEFAULT_ASSIGNMENTS"],
191 lib_dirs=[self.yosys_lib_dir],
192 lib_files=self.lint_lib_files,
193 ),
194 src_suffix=SRC_SUFFIXES,
195 source_scanner=self.verilog_src_scanner,
196 )