Coverage for hdl_registers/generator/systemverilog/axi_lite/test/test_register_file.py: 100%

134 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-10-13 20:52 +0000

1# -------------------------------------------------------------------------------------------------- 

2# Copyright (c) Lukas Vik. All rights reserved. 

3# 

4# This file is part of the hdl-registers project, an HDL register generator fast enough to run 

5# in real time. 

6# https://hdl-registers.com 

7# https://github.com/hdl-registers/hdl-registers 

8# -------------------------------------------------------------------------------------------------- 

9 

10import pytest 

11from tsfpga.system_utils import create_file, read_file 

12 

13from hdl_registers.field.enumeration import Enumeration 

14from hdl_registers.field.integer import Integer 

15from hdl_registers.field.numerical_interpretation import ( 

16 Signed, 

17 SignedFixedPoint, 

18 UnsignedFixedPoint, 

19) 

20from hdl_registers.generator.systemverilog.axi_lite.register_file import ( 

21 SystemVerilogAxiLiteGenerator, 

22) 

23from hdl_registers.register_list import RegisterList 

24from hdl_registers.register_modes import REGISTER_MODES 

25 

26 

27def get_basic_register_list(path=None): 

28 source_definition_file = None if path is None else path 

29 register_list = RegisterList(name="caesar", source_definition_file=source_definition_file) 

30 

31 register = register_list.append_register(name="reg0", mode=REGISTER_MODES["w"], description="") 

32 register.append_bit(name="bit0", description="", default_value="0") 

33 register.append_bit(name="bit1", description="", default_value="1") 

34 register.append_bit_vector(name="bit_vector0", description="", width=4, default_value="1101") 

35 register.append_bit_vector( 

36 name="bit_vector1", 

37 description="", 

38 width=4, 

39 default_value="1001", 

40 numerical_interpretation=Signed(bit_width=4), 

41 ) 

42 register.append_bit_vector( 

43 name="bit_vector2", 

44 description="", 

45 width=4, 

46 default_value="1101", 

47 numerical_interpretation=UnsignedFixedPoint(1, -2), 

48 ) 

49 register.append_bit_vector( 

50 name="bit_vector3", 

51 description="", 

52 width=4, 

53 default_value="1101", 

54 numerical_interpretation=SignedFixedPoint(2, -1), 

55 ) 

56 register.append_integer( 

57 name="int0", min_value=3, max_value=16383, default_value=16377, description="" 

58 ) 

59 

60 register = register_list.append_register(name="reg1", mode=REGISTER_MODES["r"], description="") 

61 register.append_enumeration( 

62 name="enum0", 

63 description="", 

64 elements={"element0": "", "element1": "", "element2": "", "element3": "", "element4": ""}, 

65 default_value="element1", 

66 ) 

67 register.append_bit_vector(name="bit_vector4", description="", width=1, default_value="0") 

68 register.append_bit(name="bit2", description="", default_value="1") 

69 

70 register = register_list.append_register( 

71 name="reg2", mode=REGISTER_MODES["r_w"], description="" 

72 ) 

73 register.append_bit_vector(name="bit_vector5", description="", width=6, default_value="010101") 

74 register.append_enumeration( 

75 name="enum1", description="", elements={"element5": ""}, default_value="element5" 

76 ) 

77 

78 return register_list 

79 

80 

81def test_basic_register_list(tmp_path): 

82 generator = SystemVerilogAxiLiteGenerator( 

83 register_list=get_basic_register_list(), output_folder=tmp_path 

84 ) 

85 generator.create() 

86 

87 for generated_file in generator.output_files: 

88 print(generated_file) 

89 

90 

91def test_create_if_needed(tmp_path): 

92 generator = SystemVerilogAxiLiteGenerator( 

93 register_list=get_basic_register_list(), output_folder=tmp_path 

94 ) 

95 

96 def create(expect_create: bool = True): 

97 created, _ = generator.create_if_needed() 

98 assert created == expect_create 

99 

100 create() 

101 create(expect_create=False) 

102 

103 sv = read_file(generator.output_file) 

104 create_file(generator.output_file, sv.replace("hdl-registers version ", "AAAAAAA")) 

105 create() 

106 create(expect_create=False) 

107 

108 (tmp_path / "caesar_register_file_axi_lite.sv").unlink() 

109 create() 

110 create(expect_create=False) 

111 

112 (tmp_path / "caesar_register_file_axi_lite_pkg.sv").unlink() 

113 create() 

114 create(expect_create=False) 

115 

116 

117def test_flatten(tmp_path): 

118 generator = SystemVerilogAxiLiteGenerator( 

119 register_list=get_basic_register_list(), output_folder=tmp_path 

120 ) 

121 

122 interface = "axi4lite_intf.slave s_axil," 

123 flattened = "input wire s_axil_awvalid," 

124 

125 generator.create(flatten_axi_lite=True) 

126 sv = read_file(generator.output_file) 

127 

128 assert interface not in sv 

129 assert flattened in sv 

130 

131 generator.create(flatten_axi_lite=False) 

132 sv = read_file(generator.output_file) 

133 

134 assert interface in sv 

135 assert flattened not in sv 

136 

137 

138def test_with_and_without_source_definition_file(tmp_path): 

139 """ 

140 The name of the source definition file does not seem to appear anywhere in the 

141 generated SystemVerilog code. 

142 But we set it anyway since it's part of the API. 

143 In SystemRDL it is required, but in hdl-registers it is optional, so we test both cases. 

144 """ 

145 SystemVerilogAxiLiteGenerator( 

146 register_list=get_basic_register_list(path=tmp_path), output_folder=tmp_path 

147 ).create() 

148 SystemVerilogAxiLiteGenerator( 

149 register_list=get_basic_register_list(), output_folder=tmp_path 

150 ).create() 

151 

152 

153def test_default_values_on_reset(tmp_path): 

154 register_list = get_basic_register_list() 

155 sv = read_file( 

156 SystemVerilogAxiLiteGenerator(register_list=register_list, output_folder=tmp_path).create() 

157 ) 

158 

159 for register in register_list.register_objects: 

160 for field in register.fields: 

161 reset_assign = f""" 

162 if(rst) begin 

163 field_storage.{register.name}.{field.name}.value <=""" 

164 

165 if register.mode.software_can_write: 

166 default_value_int = ( 

167 field.default_value.value 

168 if isinstance(field, Enumeration) 

169 else field.default_value 

170 if isinstance(field, Integer) 

171 else int(field.default_value, 2) 

172 ) 

173 assert f"{reset_assign} {field.width}'h{default_value_int:x};\n" in sv 

174 else: 

175 assert reset_assign not in sv 

176 

177 

178def test_field_bit_indexes(tmp_path): 

179 register_list = get_basic_register_list(path=tmp_path) 

180 sv = read_file( 

181 SystemVerilogAxiLiteGenerator(register_list=register_list, output_folder=tmp_path).create() 

182 ) 

183 

184 for register in register_list.register_objects: 

185 for field in register.fields: 

186 bits = f"{field.base_index + field.width - 1}:{field.base_index}" 

187 

188 if register.mode.software_can_write: 

189 assert ( 

190 f"next_c = " 

191 f"(field_storage.{register.name}.{field.name}.value " 

192 f"& ~decoded_wr_biten[{bits}])" 

193 ) in sv 

194 

195 if register.mode.software_can_read: 

196 value_source = "field_storage" if register.mode.software_can_write else "hwif_in" 

197 assert ( 

198 f"[{bits}] = (decoded_reg_strb.{register.name} && !decoded_req_is_wr) ? " 

199 f"{value_source}.{register.name}.{field.name}." 

200 ) in sv 

201 

202 

203def test_enumeration_naming_and_encoding(tmp_path): 

204 SystemVerilogAxiLiteGenerator( 

205 register_list=get_basic_register_list(path=tmp_path), output_folder=tmp_path 

206 ).create() 

207 sv = read_file(tmp_path / "caesar_register_file_axi_lite_pkg.sv") 

208 

209 assert ( 

210 """ 

211 typedef enum logic [2:0] { 

212 caesar_reg1_enum0__element0 = 'h0, 

213 caesar_reg1_enum0__element1 = 'h1, 

214 caesar_reg1_enum0__element2 = 'h2, 

215 caesar_reg1_enum0__element3 = 'h3, 

216 caesar_reg1_enum0__element4 = 'h4 

217 } caesar_reg1_enum0_e; 

218 

219 typedef enum logic { 

220 caesar_reg2_enum1__element5 = 'h0 

221 } caesar_reg2_enum1_e; 

222""" 

223 in sv 

224 ) 

225 

226 

227def test_empty_register_list(tmp_path): 

228 register_list = RegisterList(name="empty") 

229 _test_empty_register_list(register_list=register_list, tmp_path=tmp_path) 

230 

231 

232def _test_empty_register_list(register_list, tmp_path, extra=""): 

233 with pytest.raises(ValueError) as exception_info: 

234 SystemVerilogAxiLiteGenerator(register_list=register_list, output_folder=tmp_path).create() 

235 assert str(exception_info.value) == ( 

236 f'Error while translating "empty"{extra}: ' 

237 "SystemVerilog generator requires at least one register." 

238 ) 

239 

240 

241def test_error_message_with_and_without_source_definition_file(tmp_path): 

242 register_list = RegisterList(name="empty") 

243 _test_empty_register_list(register_list=register_list, tmp_path=tmp_path) 

244 

245 register_list = RegisterList(name="empty", source_definition_file=tmp_path) 

246 _test_empty_register_list( 

247 register_list=register_list, tmp_path=tmp_path, extra=f" in {tmp_path}" 

248 ) 

249 

250 

251def test_register_array(tmp_path): 

252 register_list = get_basic_register_list(path=tmp_path) 

253 register_list.append_register_array(name="a", length=2, description="").append_register( 

254 "b", mode=REGISTER_MODES["r"], description="" 

255 ) 

256 

257 with pytest.raises(ValueError) as exception_info: 

258 SystemVerilogAxiLiteGenerator(register_list=register_list, output_folder=tmp_path).create() 

259 assert str(exception_info.value) == ( 

260 f'Error while translating "caesar.a" in {tmp_path}: ' 

261 "SystemVerilog generator does not support register arrays." 

262 ) 

263 

264 

265def test_empty_register(tmp_path): 

266 register_list = get_basic_register_list(path=tmp_path) 

267 register_list.append_register("b", mode=REGISTER_MODES["r"], description="") 

268 

269 with pytest.raises(ValueError) as exception_info: 

270 SystemVerilogAxiLiteGenerator(register_list=register_list, output_folder=tmp_path).create() 

271 assert str(exception_info.value) == ( 

272 f'Error while translating "caesar.b" in {tmp_path}: ' 

273 "SystemVerilog generator requires at least one field per register." 

274 ) 

275 

276 

277def test_signed_integer_field(tmp_path): 

278 register_list = get_basic_register_list(path=tmp_path) 

279 register_list.append_register("b", mode=REGISTER_MODES["r"], description="").append_integer( 

280 name="c", min_value=-1, max_value=1, default_value=0, description="" 

281 ) 

282 

283 with pytest.raises(ValueError) as exception_info: 

284 SystemVerilogAxiLiteGenerator(register_list=register_list, output_folder=tmp_path).create() 

285 assert str(exception_info.value) == ( 

286 f'Error while translating "caesar.b.c" in {tmp_path}: ' 

287 "SystemVerilog generator does not support signed integer fields." 

288 ) 

289 

290 

291def test_constant(tmp_path): 

292 register_list = get_basic_register_list(path=tmp_path) 

293 register_list.add_constant(name="c", value=42, description="") 

294 

295 with pytest.raises(ValueError) as exception_info: 

296 SystemVerilogAxiLiteGenerator(register_list=register_list, output_folder=tmp_path).create() 

297 assert str(exception_info.value) == ( 

298 f'Error while translating "caesar" in {tmp_path}: ' 

299 "SystemVerilog generator does not support constants." 

300 ) 

301 

302 

303def test_register_mode_r_wpulse(tmp_path): 

304 def run_test(mode): 

305 register_list = get_basic_register_list(path=tmp_path) 

306 register_list.append_register( 

307 name="a", mode=REGISTER_MODES[mode], description="" 

308 ).append_bit(name="b", description="", default_value="1") 

309 

310 with pytest.raises(ValueError) as exception_info: 

311 SystemVerilogAxiLiteGenerator( 

312 register_list=register_list, output_folder=tmp_path 

313 ).create() 

314 assert str(exception_info.value) == ( 

315 f'Error while translating "caesar.a" in {tmp_path}: ' 

316 "SystemVerilog generator does not support " 

317 f"register mode: RegisterMode(shorthand={mode})" 

318 ) 

319 

320 run_test("r_wpulse") 

321 run_test("wpulse")