Coverage for apio/scons/plugin_ice40.py: 100%

55 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-07 05:08 +0000

1# This file is part of the Apio project 

2# (C) 2016-present FPGAwars 

3# Project authors: 

4# * Jesús Arroyo (Jesus89) (2016-2019) 

5# * Juan Gonzalez (obijuan) (2019-2024) 

6# * The Apio Team (2024-present) 

7# License: GPLv2 

8 

9"""Apio scons plugin for the ice40 architecture.""" 

10 

11# pylint: disable=duplicate-code 

12 

13from pathlib import Path 

14from typing_extensions import override 

15from SCons.Script import Builder 

16from SCons.Builder import BuilderBase, CompositeBuilder 

17from apio.common.common_util import SRC_SUFFIXES 

18from apio.scons.apio_env import ApioEnv 

19from apio.scons.plugin_base import PluginBase, ArchPluginInfo 

20from apio.scons.plugin_util import ( 

21 verilator_lint_action, 

22 has_testbench_name, 

23 announce_testbench_action, 

24 source_files_issue_scanner_action, 

25 iverilog_action, 

26 make_verilator_config_builder, 

27 get_define_flags, 

28) 

29 

30 

31class PluginIce40(PluginBase): 

32 """Apio scons plugin for the ice40 architecture.""" 

33 

34 def __init__(self, apio_env: ApioEnv): 

35 # -- Call parent constructor. 

36 super().__init__(apio_env) 

37 

38 # -- Cache values. 

39 yosys_path = Path(apio_env.params.environment.yosys_path) 

40 self.yosys_lib_dir = yosys_path / "ice40" 

41 self.sim_lib_files = [yosys_path / "ice40" / "cells_sim.v"] 

42 self.lint_lib_files = self.sim_lib_files 

43 

44 @override 

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 ) 

52 

53 @override 

54 def make_synth_builder(self) -> BuilderBase | CompositeBuilder: 

55 """Creates and returns the synth builder.""" 

56 

57 # -- Keep short references. 

58 apio_env = self.apio_env 

59 params = apio_env.params 

60 

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 ) 

76 

77 @override 

78 def make_pnr_builder(self) -> BuilderBase | CompositeBuilder: 

79 """Creates and returns the pnr builder.""" 

80 

81 # -- Keep short references. 

82 apio_env = self.apio_env 

83 params = apio_env.params 

84 

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 

90 

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_definition.ice40_params.nextpnr_type, 

98 params.fpga_definition.ice40_params.nextpnr_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 ) 

108 

109 @override 

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 ) 

117 

118 @override 

119 def make_testbench_compile_builder(self) -> BuilderBase | CompositeBuilder: 

120 """Creates and returns the testbench compile builder.""" 

121 

122 # -- Keep short references. 

123 apio_env = self.apio_env 

124 params = apio_env.params 

125 

126 # -- Sanity checks 

127 assert apio_env.targeting_one_of("sim", "test") 

128 assert params.target.HasField("sim") or params.target.HasField("test") 

129 

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 

137 

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 

155 

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 ) 

164 

165 @override 

166 def make_lint_config_builder(self) -> BuilderBase: 

167 """Creates and returns the lint config builder.""" 

168 

169 # -- Sanity checks 

170 assert self.apio_env.targeting_one_of("lint") 

171 

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 ) 

178 

179 @override 

180 def make_lint_builder(self) -> BuilderBase | CompositeBuilder: 

181 """Creates and returns the lint builder.""" 

182 

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 )