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

47 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 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.yosys_lib_file = yosys_path / "gowin" / "cells_sim.v" 

44 

45 def plugin_info(self) -> ArchPluginInfo: 

46 """Return plugin specific parameters.""" 

47 return ArchPluginInfo( 

48 constrains_file_ext=".cst", 

49 bin_file_suffix=".fs", 

50 clk_name_index=0, 

51 ) 

52 

53 # @overrides 

54 def synth_builder(self) -> BuilderBase: 

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_gowin -top {0} {1} -json $TARGET" {2} {3} ' 

65 "$SOURCES" 

66 ).format( 

67 params.apio_env_params.top_module, 

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

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

70 get_define_flags(apio_env), 

71 ), 

72 suffix=".json", 

73 src_suffix=SRC_SUFFIXES, 

74 source_scanner=self.verilog_src_scanner, 

75 ) 

76 

77 # @overrides 

78 def pnr_builder(self) -> BuilderBase: 

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-himbaechel --device {0} --json $SOURCE " 

95 "--write $TARGET --report {1} --vopt family={2} " 

96 "--vopt cst={3} {4} {5}" 

97 ).format( 

98 params.fpga_info.part_num, 

99 apio_env.target + ".pnr", 

100 params.fpga_info.gowin.family, 

101 self.constrain_file(), 

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

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

104 ), 

105 suffix=".pnr.json", 

106 src_suffix=".json", 

107 emitter=emitter, 

108 ) 

109 

110 # @overrides 

111 def bitstream_builder(self) -> BuilderBase: 

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

113 

114 return Builder( 

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

116 self.apio_env.params.fpga_info.gowin.family 

117 ), 

118 suffix=".fs", 

119 src_suffix=".pnr.json", 

120 ) 

121 

122 # @overrides 

123 def testbench_compile_builder(self) -> BuilderBase: 

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

125 

126 # -- Keep short references. 

127 apio_env = self.apio_env 

128 params = apio_env.params 

129 

130 # -- Sanity checks 

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

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

133 

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

135 # -- string for sim and test. 

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

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

138 # Extract testbench name from target file name. 

139 testbench_file = str(target[0]) 

140 assert has_testbench_name(testbench_file), testbench_file 

141 testbench_name = basename(testbench_file) 

142 

143 # Construct the actions list. 

144 action = [ 

145 # -- Print a testbench title. 

146 announce_testbench_action(), 

147 # -- Scan source files for issues. 

148 source_files_issue_scanner_action(), 

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

150 iverilog_action( 

151 apio_env, 

152 verbose=params.verbosity.all, 

153 vcd_output_name=testbench_name, 

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

155 lib_dirs=[self.yosys_lib_dir], 

156 lib_files=[self.yosys_lib_file], 

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("lint") 

176 

177 # -- Make the builder. 

178 return make_verilator_config_builder(self.yosys_lib_dir) 

179 

180 # @overrides 

181 def lint_builder(self) -> BuilderBase: 

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

183 

184 return Builder( 

185 action=verilator_lint_action( 

186 self.apio_env, 

187 lib_dirs=[self.yosys_lib_dir], 

188 lib_files=[self.yosys_lib_file], 

189 ), 

190 src_suffix=SRC_SUFFIXES, 

191 source_scanner=self.verilog_src_scanner, 

192 )