Coverage for apio/scons/plugin_gowin.py: 97%
54 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 gowin 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 PluginGowin(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 / "gowin"
42 self.sim_lib_files = [yosys_path / "gowin" / "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. The file is
45 # -- per-family (derived from the part number); a few modules are
46 # -- declared in both files — cells_sim.v goes first so its models
47 # -- win, and the duplicates are waived with the MODDUP rule below
48 # -- (scoped to the yosys lib dir).
49 self.lint_lib_files = self.sim_lib_files
50 part_num = apio_env.params.fpga_info.part_num.upper()
51 for prefix, family in ( 51 ↛ exitline 51 didn't return from function '__init__' because the loop on line 51 didn't complete
52 ("GW1N", "gw1n"),
53 ("GW2A", "gw2a"),
54 ("GW5A", "gw5a"),
55 ):
56 if part_num.startswith(prefix): 56 ↛ 51line 56 didn't jump to line 51 because the condition on line 56 was always true
57 self.lint_lib_files = self.sim_lib_files + [
58 yosys_path / "gowin" / f"cells_xtra_{family}.v"
59 ]
60 break
62 def plugin_info(self) -> ArchPluginInfo:
63 """Return plugin specific parameters."""
64 return ArchPluginInfo(
65 constrains_file_suffix=".cst",
66 pnr_file_suffix=".pnr.json",
67 bitstream_file_suffix=".fs",
68 )
70 # @overrides
71 def make_synth_builder(self) -> BuilderBase | CompositeBuilder:
72 """Creates and returns the synth builder."""
74 # -- Keep short references.
75 apio_env = self.apio_env
76 params = apio_env.params
77 gowin_params = params.fpga_info.gowin_params
79 # -- The yosys synth builder.
80 return Builder(
81 action=(
82 'yosys -p "synth_gowin -top {0} {1} -json $TARGET {2}" '
83 "{3} -DSYNTHESIZE {4} $SOURCES"
84 ).format(
85 params.apio_env_params.top_module,
86 (
87 f"-family {gowin_params.yosys_family}"
88 if gowin_params.yosys_family
89 else ""
90 ),
91 " ".join(params.apio_env_params.yosys_extra_options),
92 "" if params.verbosity.all or params.verbosity.synth else "-q",
93 get_define_flags(apio_env),
94 ),
95 source_scanner=self.verilog_src_scanner,
96 src_suffix=SRC_SUFFIXES,
97 suffix=".json",
98 )
100 # @overrides
101 def make_pnr_builder(self) -> BuilderBase | CompositeBuilder:
102 """Creates and returns the pnr builder."""
104 # -- Keep short references.
105 apio_env = self.apio_env
106 params = apio_env.params
107 gowin_params = params.fpga_info.gowin_params
109 # -- We use an emmiter to add to the builder a second output file.
110 def emitter(target, source, env):
111 _ = env # Unused
112 target.append(apio_env.target + ".pnr")
113 return target, source
115 # -- Create the builder.
116 return Builder(
117 action=(
118 "nextpnr-himbaechel --device {0} --json $SOURCE "
119 "--write $TARGET --report {1} {2} "
120 "--vopt cst={3} {4} {5}"
121 ).format(
122 params.fpga_info.part_num,
123 apio_env.target + ".pnr",
124 (
125 f"--vopt family={gowin_params.nextpnr_family}"
126 if gowin_params.nextpnr_family
127 else ""
128 ),
129 self.constrain_file(),
130 "" if params.verbosity.all or params.verbosity.pnr else "-q",
131 " ".join(params.apio_env_params.nextpnr_extra_options),
132 ),
133 src_suffix=".json",
134 suffix=".pnr.json",
135 emitter=emitter,
136 )
138 # @overrides
139 def make_bitstream_builder(self) -> BuilderBase | CompositeBuilder:
140 """Creates and returns the bitstream builder."""
142 return Builder(
143 action="gowin_pack -d {0} -o $TARGET $SOURCE".format(
144 self.apio_env.params.fpga_info.gowin_params.packer_device
145 ),
146 src_suffix=".pnr.json",
147 suffix=".fs",
148 )
150 # @overrides
151 def make_testbench_compile_builder(self) -> BuilderBase | CompositeBuilder:
152 """Creates and returns the testbench compile builder."""
154 # -- Keep short references.
155 apio_env = self.apio_env
156 params = apio_env.params
158 # -- Sanity checks
159 assert apio_env.targeting_one_of("sim", "test")
160 assert params.target.HasField("sim") or params.target.HasField("test")
162 # -- We use a generator because we need a different action
163 # -- string for sim and test.
164 def action_generator(target, source, env, for_signature):
165 _ = (source, env, for_signature) # Unused
166 # Extract testbench file name from the target.
167 testbench_file = str(target[0])
168 assert has_testbench_name(testbench_file), testbench_file
170 # Construct the actions list.
171 action = [
172 # -- Print a testbench title.
173 announce_testbench_action(),
174 # -- Scan source files for issues.
175 source_files_issue_scanner_action(),
176 # -- Perform the actual test or sim compilation.
177 iverilog_action(
178 apio_env,
179 verbose=params.verbosity.all,
180 is_interactive=apio_env.targeting_one_of("sim"),
181 lib_dirs=[self.yosys_lib_dir],
182 lib_files=self.sim_lib_files,
183 ),
184 ]
185 return action
187 # -- The testbench compiler builder.
188 return Builder(
189 # -- Dynamic action string generator.
190 generator=action_generator,
191 source_scanner=self.verilog_src_scanner,
192 src_suffix=SRC_SUFFIXES,
193 suffix=".out",
194 )
196 # @overrides
197 def make_lint_config_builder(self) -> BuilderBase | CompositeBuilder:
198 """Creates and returns the lint config builder."""
200 # -- Sanity checks
201 assert self.apio_env.targeting_one_of("lint")
203 # -- Make the builder.
204 # -- See https://verilator.org/guide/latest/warnings.html
205 return make_verilator_config_builder(
206 self.yosys_lib_dir,
207 rules_to_suppress=[
208 "SPECIFYIGN",
209 # -- cells_xtra_gw2a/gw5a re-declare a few modules that also
210 # -- have simulation models in cells_sim.v (DQS, IDES4_MEM,
211 # -- OSER4_MEM); verilator keeps the first definition.
212 "MODDUP",
213 ],
214 )
216 # @overrides
217 def make_lint_builder(self) -> BuilderBase | CompositeBuilder:
218 """Creates and returns the lint builder."""
220 return Builder(
221 action=verilator_lint_action(
222 self.apio_env,
223 lib_dirs=[self.yosys_lib_dir],
224 lib_files=self.lint_lib_files,
225 ),
226 source_scanner=self.verilog_src_scanner,
227 src_suffix=SRC_SUFFIXES,
228 )