Coverage for apio/scons/plugin_ice40.py: 100%
47 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 ice40 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 PluginIce40(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 yosys_path = Path(apio_env.params.environment.yosys_path)
41 self.yosys_lib_dir = yosys_path / "ice40"
42 self.sim_lib_files = [yosys_path / "ice40" / "cells_sim.v"]
43 self.lint_lib_files = self.sim_lib_files
45 def plugin_info(self) -> ArchPluginInfo:
46 """Return plugin specific parameters."""
47 return ArchPluginInfo(
48 constrains_file_suffix=".pcf",
49 pnr_file_suffix=".asc",
50 bitstream_file_suffix=".bin",
51 )
53 # @overrides
54 def make_synth_builder(self) -> BuilderBase | CompositeBuilder:
55 """Creates and returns the synth builder."""
57 # -- Keep short references.
58 apio_env = self.apio_env
59 params = apio_env.params
61 # -- The yosys synth builder.
62 return Builder(
63 action=(
64 'yosys -p "synth_ice40 -top {0} -json $TARGET {1}" '
65 "{2} -DSYNTHESIZE {3} $SOURCES"
66 ).format(
67 params.apio_env_params.top_module,
68 " ".join(params.apio_env_params.yosys_extra_options),
69 "" if params.verbosity.all or params.verbosity.synth else "-q",
70 get_define_flags(apio_env),
71 ),
72 source_scanner=self.verilog_src_scanner,
73 src_suffix=SRC_SUFFIXES,
74 suffix=".json",
75 )
77 # @overrides
78 def make_pnr_builder(self) -> BuilderBase | CompositeBuilder:
79 """Creates and returns the pnr builder."""
81 # -- Keep short references.
82 apio_env = self.apio_env
83 params = apio_env.params
85 # -- We use an emmiter to add to the builder a second output file.
86 def emitter(target, source, env):
87 _ = env # Unused
88 target.append(apio_env.target + ".pnr")
89 return target, source
91 # -- Create the builder.
92 return Builder(
93 action=(
94 "nextpnr-ice40 --{0} --package {1} --json $SOURCE "
95 "--asc $TARGET --report {2} --pcf {3} {4} {5}"
96 ).format(
97 params.fpga_info.ice40_params.type,
98 params.fpga_info.ice40_params.package,
99 apio_env.target + ".pnr",
100 self.constrain_file(),
101 "" if params.verbosity.all or params.verbosity.pnr else "-q",
102 " ".join(params.apio_env_params.nextpnr_extra_options),
103 ),
104 src_suffix=".json",
105 suffix=".asc",
106 emitter=emitter,
107 )
109 # @overrides
110 def make_bitstream_builder(self) -> BuilderBase | CompositeBuilder:
111 """Creates and returns the bitstream builder."""
112 return Builder(
113 action="icepack $SOURCE $TARGET",
114 src_suffix=".asc",
115 suffix=".bin",
116 )
118 # @overrides
119 def make_testbench_compile_builder(self) -> BuilderBase | CompositeBuilder:
120 """Creates and returns the testbench compile builder."""
122 # -- Keep short references.
123 apio_env = self.apio_env
124 params = apio_env.params
126 # -- Sanity checks
127 assert apio_env.targeting_one_of("sim", "test")
128 assert params.target.HasField("sim") or params.target.HasField("test")
130 # -- We use a generator because we need a different action
131 # -- string for sim and test.
132 def action_generator(target, source, env, for_signature):
133 _ = (source, env, for_signature) # Unused
134 # Extract testbench file name from the target.
135 testbench_file = str(target[0])
136 assert has_testbench_name(testbench_file), testbench_file
138 # Construct the actions list.
139 action = [
140 # -- Print a testbench title.
141 announce_testbench_action(),
142 # -- Scan source files for issues.
143 source_files_issue_scanner_action(),
144 # -- Perform the actual test or sim compilation.
145 iverilog_action(
146 apio_env,
147 verbose=params.verbosity.all,
148 is_interactive=apio_env.targeting_one_of("sim"),
149 extra_params=["-DNO_ICE40_DEFAULT_ASSIGNMENTS"],
150 lib_dirs=[self.yosys_lib_dir],
151 lib_files=self.sim_lib_files,
152 ),
153 ]
154 return action
156 # -- The testbench compiler builder.
157 return Builder(
158 # -- Dynamic action string generator.
159 generator=action_generator,
160 source_scanner=self.verilog_src_scanner,
161 src_suffix=SRC_SUFFIXES,
162 suffix=".out",
163 )
165 # @overrides
166 def make_lint_config_builder(self) -> BuilderBase:
167 """Creates and returns the lint config builder."""
169 # -- Sanity checks
170 assert self.apio_env.targeting_one_of("lint")
172 # -- Make the builder.
173 # -- See https://verilator.org/guide/latest/warnings.html
174 return make_verilator_config_builder(
175 self.yosys_lib_dir,
176 rules_to_suppress=[],
177 )
179 # @overrides
180 def make_lint_builder(self) -> BuilderBase | CompositeBuilder:
181 """Creates and returns the lint builder."""
183 return Builder(
184 action=verilator_lint_action(
185 self.apio_env,
186 extra_params=["-DNO_ICE40_DEFAULT_ASSIGNMENTS"],
187 lib_dirs=[self.yosys_lib_dir],
188 lib_files=self.lint_lib_files,
189 ),
190 source_scanner=self.verilog_src_scanner,
191 src_suffix=SRC_SUFFIXES,
192 )