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

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 

10 

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

12 

13# pylint: disable=duplicate-code 

14 

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) 

31 

32 

33class PluginIce40(PluginBase): 

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

35 

36 def __init__(self, apio_env: ApioEnv): 

37 # -- Call parent constructor. 

38 super().__init__(apio_env) 

39 

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 

45 

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 ) 

54 

55 # @overrides 

56 def synth_builder(self) -> BuilderBase: 

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

58 

59 # -- Keep short references. 

60 apio_env = self.apio_env 

61 params = apio_env.params 

62 

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 ) 

78 

79 # @overrides 

80 def pnr_builder(self) -> BuilderBase: 

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

82 

83 # -- Keep short references. 

84 apio_env = self.apio_env 

85 params = apio_env.params 

86 

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 

92 

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 ) 

111 

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 ) 

120 

121 # @overrides 

122 def testbench_compile_builder(self) -> BuilderBase: 

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

124 

125 # -- Keep short references. 

126 apio_env = self.apio_env 

127 params = apio_env.params 

128 

129 # -- Sanity checks 

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

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

132 

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) 

141 

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 

160 

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 ) 

169 

170 # @overrides 

171 def lint_config_builder(self) -> BuilderBase: 

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

173 

174 # -- Sanity checks 

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

176 

177 # -- Make the builder. 

178 return make_verilator_config_builder( 

179 self.yosys_lib_dir, 

180 rules_to_supress=[], 

181 ) 

182 

183 # @overrides 

184 def lint_builder(self) -> BuilderBase: 

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

186 

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 )