Coverage for hdl_registers/field/test/test_integer.py: 100%

136 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-12 11:11 +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 

10from copy import copy 

11 

12import pytest 

13 

14from hdl_registers.field.integer import Integer 

15 

16TEST_FIELD = Integer( 

17 name="apa", 

18 base_index=3, 

19 description="hest", 

20 min_value=120, 

21 max_value=456, 

22 default_value=127, 

23) 

24 

25 

26def test_fields(): 

27 assert TEST_FIELD.name == "apa" 

28 assert TEST_FIELD.base_index == 3 

29 assert TEST_FIELD.description == "hest" 

30 assert TEST_FIELD.min_value == 120 

31 assert TEST_FIELD.max_value == 456 

32 assert TEST_FIELD.default_value == 127 

33 

34 

35def test_repr_is_an_actual_representation(): 

36 # Check that repr is an actual representation, not just "X object at 0xABCDEF" 

37 assert "apa" in repr(TEST_FIELD) 

38 

39 

40def test_repr_is_same_after_copy(): 

41 field = copy(TEST_FIELD) 

42 

43 assert repr(field) == repr(TEST_FIELD) 

44 

45 

46def test_repr_should_change_when_name_is_changed(): 

47 field = copy(TEST_FIELD) 

48 field.name = "zebra" 

49 

50 assert repr(field) != repr(TEST_FIELD) 

51 

52 

53def test_repr_should_change_when_default_value_is_changed(): 

54 field = copy(TEST_FIELD) 

55 field.default_value = 133 

56 

57 assert repr(field) != repr(TEST_FIELD) 

58 

59 

60def test_repr_when_static_members_have_different_value(): 

61 original_field = Integer( 

62 name="apa", 

63 base_index=0, 

64 description="", 

65 min_value=10, 

66 max_value=10, 

67 default_value=10, 

68 ) 

69 

70 # Different base_index 

71 assert repr( 

72 Integer( 

73 name="apa", 

74 base_index=25, 

75 description="", 

76 min_value=10, 

77 max_value=10, 

78 default_value=10, 

79 ) 

80 ) != repr(original_field) 

81 

82 # Different description 

83 assert repr( 

84 Integer( 

85 name="apa", 

86 base_index=0, 

87 description="blah", 

88 min_value=10, 

89 max_value=10, 

90 default_value=10, 

91 ) 

92 ) != repr(original_field) 

93 

94 # Different min_value 

95 assert repr( 

96 Integer( 

97 name="apa", 

98 base_index=0, 

99 description="", 

100 min_value=5, 

101 max_value=10, 

102 default_value=10, 

103 ) 

104 ) != repr(original_field) 

105 

106 # Different max_value 

107 assert repr( 

108 Integer( 

109 name="apa", 

110 base_index=0, 

111 description="", 

112 min_value=10, 

113 max_value=15, 

114 default_value=10, 

115 ) 

116 ) != repr(original_field) 

117 

118 

119def test_is_signed(): 

120 def get_is_signed(min_value, max_value): 

121 return Integer( 

122 name="", 

123 base_index=0, 

124 description="", 

125 min_value=min_value, 

126 max_value=max_value, 

127 default_value=min_value, 

128 ).is_signed 

129 

130 assert get_is_signed(min_value=-10, max_value=10) 

131 assert get_is_signed(min_value=-10, max_value=-5) 

132 assert not get_is_signed(min_value=0, max_value=10) 

133 assert not get_is_signed(min_value=5, max_value=10) 

134 

135 

136def test_non_ascending_range_should_raise_exception(): 

137 with pytest.raises(ValueError) as exception_info: 

138 Integer( 

139 name="apa", base_index=0, description="", min_value=10, max_value=0, default_value=0 

140 ) 

141 

142 assert ( 

143 str(exception_info.value) 

144 == 'Integer field "apa" should have ascending range. Got: [10, 0].' 

145 ) 

146 

147 

148def test_non_integer_range_should_raise_exception(): 

149 with pytest.raises(TypeError) as exception_info: 

150 Integer( 

151 name="apa", 

152 base_index=0, 

153 description="", 

154 min_value="5", 

155 max_value=10, 

156 default_value=11, 

157 ) 

158 assert ( 

159 str(exception_info.value) 

160 == 'Integer field "apa" should have integer value for "min_value". Got: "5".' 

161 ) 

162 

163 with pytest.raises(TypeError) as exception_info: 

164 Integer( 

165 name="apa", 

166 base_index=0, 

167 description="", 

168 min_value=5, 

169 max_value="X", 

170 default_value=11, 

171 ) 

172 assert ( 

173 str(exception_info.value) 

174 == 'Integer field "apa" should have integer value for "max_value". Got: "X".' 

175 ) 

176 

177 

178def test_get_value_unsigned(): 

179 integer = Integer( 

180 name="", base_index=2, min_value=0, max_value=127, description="", default_value=0 

181 ) 

182 assert integer.width == 7 

183 

184 register_value = int("0101010_11", base=2) 

185 assert integer.get_value(register_value) == 42 

186 

187 register_value = int("1010101_00", base=2) 

188 assert integer.get_value(register_value) == 85 

189 

190 

191def test_get_value_signed(): 

192 integer = Integer( 

193 name="", base_index=3, min_value=-128, max_value=127, description="", default_value=0 

194 ) 

195 assert integer.width == 8 

196 

197 register_value = int("10101010_111", base=2) 

198 assert integer.get_value(register_value) == -86 

199 

200 register_value = int("01010101_000", base=2) 

201 assert integer.get_value(register_value) == 85 

202 

203 

204def test_get_value_should_raise_exception_if_value_out_of_range(): 

205 integer = Integer( 

206 name="apa", base_index=0, min_value=0, max_value=4, description="", default_value=0 

207 ) 

208 assert integer.width == 3 

209 

210 with pytest.raises(ValueError) as exception_info: 

211 integer.get_value(7) 

212 assert ( 

213 str(exception_info.value) 

214 == 'Register field value "7" not inside "apa" field\'s legal range: (0, 4).' 

215 ) 

216 

217 

218def test_set_value_unsigned(): 

219 integer = Integer( 

220 name="", base_index=5, min_value=0, max_value=7, description="", default_value=0 

221 ) 

222 assert integer.width == 3 

223 

224 assert integer.set_value(5) == int("101_00000", base=2) 

225 assert integer.set_value(2) == int("010_00000", base=2) 

226 

227 

228def test_set_value_signed(): 

229 integer = Integer( 

230 name="", base_index=5, min_value=-8, max_value=7, description="", default_value=0 

231 ) 

232 assert integer.width == 4 

233 

234 assert integer.set_value(5) == int("0101_00000", base=2) 

235 assert integer.set_value(-6) == int("1010_00000", base=2) 

236 

237 

238def test_set_value_should_raise_exception_if_value_out_of_range(): 

239 integer = Integer( 

240 name="apa", base_index=5, min_value=-1, max_value=7, description="", default_value=0 

241 ) 

242 

243 with pytest.raises(ValueError) as exception_info: 

244 integer.set_value(-8) 

245 assert str(exception_info.value) == 'Value "-8" not inside "apa" field\'s legal range: (-1, 7).' 

246 

247 

248def test_default_value_uint(): 

249 def _get_default_value_uint(min_value, max_value, default_value): 

250 return Integer( 

251 name="", 

252 base_index=0, 

253 description="", 

254 min_value=min_value, 

255 max_value=max_value, 

256 default_value=default_value, 

257 ).default_value_uint 

258 

259 assert _get_default_value_uint(min_value=5, max_value=10, default_value=7) == 7 

260 assert _get_default_value_uint(min_value=-10, max_value=10, default_value=3) == 3 

261 

262 # Negative values, converted to positive and sign extended to the width of the field. 

263 assert _get_default_value_uint(min_value=-10, max_value=10, default_value=-9) == 0b10111 

264 assert _get_default_value_uint(min_value=-10, max_value=10, default_value=-6) == 0b11010 

265 

266 

267def test_default_value_of_bad_type_should_raise_exception(): 

268 with pytest.raises(TypeError) as exception_info: 

269 Integer( 

270 name="apa", 

271 base_index=0, 

272 description="", 

273 min_value=5, 

274 max_value=10, 

275 default_value="7", 

276 ) 

277 assert ( 

278 str(exception_info.value) 

279 == 'Integer field "apa" should have integer value for "default_value". Got: "7".' 

280 ) 

281 

282 field = Integer( 

283 name="apa", 

284 base_index=0, 

285 description="", 

286 min_value=5, 

287 max_value=10, 

288 default_value=5, 

289 ) 

290 

291 with pytest.raises(TypeError) as exception_info: 

292 field.default_value = "8" 

293 assert ( 

294 str(exception_info.value) 

295 == 'Integer field "apa" should have integer value for "default_value". Got: "8".' 

296 ) 

297 

298 

299def test_default_value_out_of_range_should_raise_exception(): 

300 with pytest.raises(ValueError) as exception_info: 

301 Integer( 

302 name="apa", 

303 base_index=0, 

304 description="", 

305 min_value=5, 

306 max_value=10, 

307 default_value=11, 

308 ) 

309 assert ( 

310 str(exception_info.value) 

311 == 'Integer field "apa" should have "default_value" within range [5, 10]. Got: "11".' 

312 ) 

313 

314 field = Integer( 

315 name="apa", 

316 base_index=0, 

317 description="", 

318 min_value=5, 

319 max_value=10, 

320 default_value=5, 

321 ) 

322 

323 with pytest.raises(ValueError) as exception_info: 

324 field.default_value = 120 

325 assert ( 

326 str(exception_info.value) 

327 == 'Integer field "apa" should have "default_value" within range [5, 10]. Got: "120".' 

328 ) 

329 

330 

331def _get_field_width(min_value, max_value): 

332 return Integer( 

333 name="", 

334 base_index=0, 

335 description="", 

336 min_value=min_value, 

337 max_value=max_value, 

338 default_value=min_value, 

339 ).width 

340 

341 

342def test_unsigned_width(): 

343 assert _get_field_width(min_value=0, max_value=127) == 7 

344 assert _get_field_width(min_value=0, max_value=128) == 8 

345 assert _get_field_width(min_value=0, max_value=255) == 8 

346 assert _get_field_width(min_value=0, max_value=256) == 9 

347 

348 # The lower bound of the range does not affect the width 

349 # (but it will add checkers in our generated code). 

350 assert _get_field_width(min_value=255, max_value=255) == 8 

351 

352 

353def test_signed_width(): 

354 # Positive range has greater demand than negative 

355 assert _get_field_width(min_value=-4, max_value=16) == 5 + 1 

356 assert _get_field_width(min_value=-4, max_value=4) == 3 + 1 

357 

358 # Negative range has greater demand than positive 

359 assert _get_field_width(min_value=-7, max_value=2) == 4 

360 assert _get_field_width(min_value=-8, max_value=2) == 4 

361 assert _get_field_width(min_value=-9, max_value=2) == 5 

362 

363 assert _get_field_width(min_value=-15, max_value=7) == 5 

364 assert _get_field_width(min_value=-16, max_value=7) == 5 

365 assert _get_field_width(min_value=-17, max_value=7) == 6 

366 

367 # The upper bound of the range here does not affect the width 

368 # (but it will add checkers in our generated code). 

369 assert _get_field_width(min_value=-8, max_value=-3) == 4 

370 assert _get_field_width(min_value=-16, max_value=-4) == 5 

371 

372 

373def test_width_out_of_range_should_raise_exception(): 

374 def _test_width_out_of_range(min_value, max_value): 

375 with pytest.raises(ValueError) as exception_info: 

376 _get_field_width(min_value=min_value, max_value=max_value) 

377 assert ( 

378 str(exception_info.value) 

379 == f"Supplied integer range [{min_value}, {max_value}] does not fit in a register." 

380 ) 

381 

382 # Unsigned. Just within range, should not raise exception. 

383 _get_field_width(min_value=128, max_value=2**32 - 1) 

384 # Just outside of range. 

385 _test_width_out_of_range(min_value=128, max_value=2**32) 

386 

387 # Signed, limited by negative range. Just within range, should not raise exception. 

388 _get_field_width(min_value=-(2**31), max_value=128) 

389 # Just outside of range. 

390 _test_width_out_of_range(min_value=-(2**31) - 1, max_value=128) 

391 

392 # Signed, limited by positive range. Just within range, should not raise exception. 

393 _get_field_width(min_value=-128, max_value=2**31 - 1) 

394 # Just outside of range. 

395 _test_width_out_of_range(min_value=-128, max_value=2**31)