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

50 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-26 02:38 +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 gowin 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 PluginGowin(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 / "gowin" 

43 self.sim_lib_files = [yosys_path / "gowin" / "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=".cst", 

50 pnr_file_suffix=".pnr.json", 

51 bitstream_file_suffix=".fs", 

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 gowin_params = params.fpga_info.gowin_params 

63 

64 # -- The yosys synth builder. 

65 return Builder( 

66 action=( 

67 'yosys -p "synth_gowin -top {0} {1} -json $TARGET {2}" ' 

68 "{3} -DSYNTHESIZE {4} $SOURCES" 

69 ).format( 

70 params.apio_env_params.top_module, 

71 ( 

72 f"-family {gowin_params.yosys_family}" 

73 if gowin_params.yosys_family 

74 else "" 

75 ), 

76 " ".join(params.apio_env_params.yosys_extra_options), 

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

78 get_define_flags(apio_env), 

79 ), 

80 suffix=".json", 

81 src_suffix=SRC_SUFFIXES, 

82 source_scanner=self.verilog_src_scanner, 

83 ) 

84 

85 # @overrides 

86 def pnr_builder(self) -> BuilderBase: 

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

88 

89 # -- Keep short references. 

90 apio_env = self.apio_env 

91 params = apio_env.params 

92 gowin_params = params.fpga_info.gowin_params 

93 

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

95 def emitter(target, source, env): 

96 _ = env # Unused 

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

98 return target, source 

99 

100 # -- Create the builder. 

101 return Builder( 

102 action=( 

103 "nextpnr-himbaechel --device {0} --json $SOURCE " 

104 "--write $TARGET --report {1} {2} " 

105 "--vopt cst={3} {4} {6} {5}" 

106 ).format( 

107 params.fpga_info.part_num, 

108 apio_env.target + ".pnr", 

109 ( 

110 f"--vopt family={gowin_params.nextpnr_family}" 

111 if gowin_params.nextpnr_family 

112 else "" 

113 ), 

114 self.constrain_file(), 

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

116 "--gui" if params.nextpnr_gui else "", 

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

118 ), 

119 suffix=".pnr.json", 

120 src_suffix=".json", 

121 emitter=emitter, 

122 ) 

123 

124 # @overrides 

125 def bitstream_builder(self) -> BuilderBase: 

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

127 

128 return Builder( 

129 action="gowin_pack -d {0} -o $TARGET $SOURCE".format( 

130 self.apio_env.params.fpga_info.gowin_params.packer_device 

131 ), 

132 suffix=".fs", 

133 src_suffix=".pnr.json", 

134 ) 

135 

136 # @overrides 

137 def testbench_compile_builder(self) -> BuilderBase: 

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

139 

140 # -- Keep short references. 

141 apio_env = self.apio_env 

142 params = apio_env.params 

143 

144 # -- Sanity checks 

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

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

147 

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

149 # -- string for sim and test. 

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

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

152 # Extract testbench name from target file name. 

153 testbench_file = str(target[0]) 

154 assert has_testbench_name(testbench_file), testbench_file 

155 testbench_name = basename(testbench_file) 

156 

157 # Construct the actions list. 

158 action = [ 

159 # -- Print a testbench title. 

160 announce_testbench_action(), 

161 # -- Scan source files for issues. 

162 source_files_issue_scanner_action(), 

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

164 iverilog_action( 

165 apio_env, 

166 verbose=params.verbosity.all, 

167 vcd_output_name=testbench_name, 

168 is_interactive=apio_env.targeting_one_of("sim"), 

169 lib_dirs=[self.yosys_lib_dir], 

170 lib_files=self.sim_lib_files, 

171 ), 

172 ] 

173 return action 

174 

175 # -- The testbench compiler builder. 

176 return Builder( 

177 # -- Dynamic action string generator. 

178 generator=action_generator, 

179 suffix=".out", 

180 src_suffix=SRC_SUFFIXES, 

181 source_scanner=self.verilog_src_scanner, 

182 ) 

183 

184 # @overrides 

185 def lint_config_builder(self) -> BuilderBase: 

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

187 

188 # -- Sanity checks 

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

190 

191 # -- Make the builder. 

192 return make_verilator_config_builder( 

193 self.yosys_lib_dir, 

194 rules_to_supress=[ 

195 "SPECIFYIGN", 

196 ], 

197 ) 

198 

199 # @overrides 

200 def lint_builder(self) -> BuilderBase: 

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

202 

203 return Builder( 

204 action=verilator_lint_action( 

205 self.apio_env, 

206 lib_dirs=[self.yosys_lib_dir], 

207 lib_files=self.lint_lib_files, 

208 ), 

209 src_suffix=SRC_SUFFIXES, 

210 source_scanner=self.verilog_src_scanner, 

211 )