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

50 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-06 10:20 +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 ecp5 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 PluginEcp5(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 trellis_path = Path(apio_env.params.environment.trellis_path) 

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

43 

44 self.database_path = trellis_path / "database" 

45 self.yosys_lib_dir = yosys_path / "ecp5" 

46 self.yosys_lib_file = yosys_path / "ecp5" / "cells_sim.v" 

47 # -- For black-box cells such as EHXPLLL PLL. 

48 self.yosys_bb_lib_file = yosys_path / "ecp5" / "cells_bb.v" 

49 

50 def plugin_info(self) -> ArchPluginInfo: 

51 """Return plugin specific parameters.""" 

52 return ArchPluginInfo( 

53 constrains_file_ext=".lpf", 

54 bin_file_suffix=".bit", 

55 clk_name_index=2, 

56 ) 

57 

58 # @overrides 

59 def synth_builder(self) -> BuilderBase: 

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

61 

62 # -- Keep short references. 

63 apio_env = self.apio_env 

64 params = apio_env.params 

65 

66 # -- The yosys synth builder. 

67 return Builder( 

68 action=( 

69 'yosys -p "synth_ecp5 -top {0} {1} -json $TARGET" {2} {3} ' 

70 "$SOURCES" 

71 ).format( 

72 params.apio_env_params.top_module, 

73 " ".join(params.apio_env_params.yosys_synth_extra_options), 

74 "" if params.verbosity.all or params.verbosity.synth else "-q", 

75 get_define_flags(apio_env), 

76 ), 

77 suffix=".json", 

78 src_suffix=SRC_SUFFIXES, 

79 source_scanner=self.verilog_src_scanner, 

80 ) 

81 

82 # @overrides 

83 def pnr_builder(self) -> BuilderBase: 

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

85 

86 # -- Keep short references. 

87 apio_env = self.apio_env 

88 params = apio_env.params 

89 

90 # -- We use an emmiter to add to the builder a second output file. 

91 def emitter(target, source, env): 

92 _ = env # Unused 

93 target.append(apio_env.target + ".pnr") 

94 return target, source 

95 

96 # -- Create the builder. 

97 return Builder( 

98 action=( 

99 "nextpnr-ecp5 --{0} --package {1} --speed {2} " 

100 "--json $SOURCE --textcfg $TARGET " 

101 "--report {3} --lpf {4} {5} {6} --timing-allow-fail --force" 

102 ).format( 

103 params.fpga_info.ecp5.type, 

104 params.fpga_info.ecp5.pack, 

105 params.fpga_info.ecp5.speed, 

106 apio_env.target + ".pnr", 

107 self.constrain_file(), 

108 "" if params.verbosity.all or params.verbosity.pnr else "-q", 

109 " ".join(params.apio_env_params.nextpnr_extra_options), 

110 ), 

111 suffix=".config", 

112 src_suffix=".json", 

113 emitter=emitter, 

114 ) 

115 

116 # @overrides 

117 def bitstream_builder(self) -> BuilderBase: 

118 """Creates and returns the bitstream builder.""" 

119 

120 return Builder( 

121 action="ecppack --compress --db {0} $SOURCE $TARGET".format( 

122 self.database_path, 

123 ), 

124 suffix=".bit", 

125 src_suffix=".config", 

126 ) 

127 

128 # @overrides 

129 def testbench_compile_builder(self) -> BuilderBase: 

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

131 # -- Keep short references. 

132 apio_env = self.apio_env 

133 params = apio_env.params 

134 

135 # -- Sanity checks 

136 assert apio_env.targeting("sim", "test") 

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

138 

139 # -- We use a generator because we need a different action 

140 # -- string for sim and test. 

141 def action_generator(source, target, env, for_signature): 

142 _ = (source, env, for_signature) # Unused 

143 # Extract testbench name from target file name. 

144 testbench_file = str(target[0]) 

145 assert has_testbench_name(testbench_file), testbench_file 

146 testbench_name = basename(testbench_file) 

147 

148 # Construct the actions list. 

149 action = [ 

150 # -- Print a testbench title. 

151 announce_testbench_action(), 

152 # -- Scan source files for issues. 

153 source_files_issue_scanner_action(), 

154 # -- Perform the actual test or sim compilation. 

155 iverilog_action( 

156 apio_env, 

157 verbose=params.verbosity.all, 

158 vcd_output_name=testbench_name, 

159 is_interactive=apio_env.targeting("sim"), 

160 lib_dirs=[self.yosys_lib_dir], 

161 lib_files=[self.yosys_lib_file], 

162 ), 

163 ] 

164 return action 

165 

166 # -- The testbench compiler builder. 

167 return Builder( 

168 # -- Dynamic action string generator. 

169 generator=action_generator, 

170 suffix=".out", 

171 src_suffix=SRC_SUFFIXES, 

172 source_scanner=self.verilog_src_scanner, 

173 ) 

174 

175 # @overrides 

176 def lint_config_builder(self) -> BuilderBase: 

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

178 

179 # -- Sanity checks 

180 assert self.apio_env.targeting("lint") 

181 

182 # -- Make the builder. 

183 return make_verilator_config_builder(self.yosys_lib_dir) 

184 

185 # @overrides 

186 def lint_builder(self) -> BuilderBase: 

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

188 

189 return Builder( 

190 action=verilator_lint_action( 

191 self.apio_env, 

192 lib_dirs=[self.yosys_lib_dir], 

193 lib_files=[self.yosys_lib_file, self.yosys_bb_lib_file], 

194 ), 

195 src_suffix=SRC_SUFFIXES, 

196 source_scanner=self.verilog_src_scanner, 

197 )