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

145 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-29 06:41 +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 

11 

12from hdl_registers.field.bit_vector import BitVector 

13from hdl_registers.field.numerical_interpretation import ( 

14 Signed, 

15 SignedFixedPoint, 

16 Unsigned, 

17 UnsignedFixedPoint, 

18) 

19 

20 

21def test_get_value_plain(): 

22 field = BitVector(name="", base_index=2, description="", width=4, default_value="0000") 

23 

24 register_value = int("1110000_11", base=2) 

25 assert field.get_value(register_value) == 0 

26 

27 register_value = int("0001111_00", base=2) 

28 assert field.get_value(register_value) == 15 

29 

30 register_value = int("1010101_01", base=2) 

31 assert field.get_value(register_value) == 5 

32 

33 

34def test_get_value_fixed(): 

35 field = BitVector( 

36 name="", 

37 base_index=2, 

38 description="", 

39 width=16, 

40 default_value="0" * 16, 

41 numerical_interpretation=SignedFixedPoint.from_bit_widths( 

42 integer_bit_width=8, fraction_bit_width=8 

43 ), 

44 ) 

45 register_value = 0b11111111_00000011_11111111_11111100 

46 assert field.get_value(register_value) == -0.00390625 

47 

48 

49def test_set_value(): 

50 bit_vector = BitVector( 

51 name="", base_index=0, description="", width=2, default_value=format(0, "02b") 

52 ) 

53 assert bit_vector.set_value(0b10) == 0b10 

54 assert bit_vector.set_value(0b11) == 0b11 

55 

56 with pytest.raises(ValueError): 

57 bit_vector.set_value(0b111) 

58 

59 bit_vector = BitVector( 

60 name="", base_index=2, description="", width=2, default_value=format(0, "02b") 

61 ) 

62 assert bit_vector.set_value(0b10) == 0b10_00 

63 

64 bit_vector = BitVector( 

65 name="", base_index=3, description="", width=4, default_value=format(0, "04b") 

66 ) 

67 assert bit_vector.set_value(0b1111) == 0b1111_000 

68 

69 bit_vector0 = BitVector(name="", base_index=0, description="", width=2, default_value="00") 

70 bit_vector1 = BitVector(name="", base_index=2, description="", width=4, default_value="0000") 

71 bit_vector2 = BitVector(name="", base_index=6, description="", width=3, default_value="000") 

72 

73 register_value = int("111000011", base=2) 

74 value0 = bit_vector0.set_value(bit_vector0.get_value(register_value)) 

75 value1 = bit_vector1.set_value(bit_vector1.get_value(register_value)) 

76 value2 = bit_vector2.set_value(bit_vector2.get_value(register_value)) 

77 assert value0 | value1 | value2 == register_value 

78 

79 register_value = int("000111100", base=2) 

80 value0 = bit_vector0.set_value(bit_vector0.get_value(register_value)) 

81 value1 = bit_vector1.set_value(bit_vector1.get_value(register_value)) 

82 value2 = bit_vector2.set_value(bit_vector2.get_value(register_value)) 

83 assert value0 | value1 | value2 == register_value 

84 

85 register_value = int("101010101", base=2) 

86 value0 = bit_vector0.set_value(bit_vector0.get_value(register_value)) 

87 value1 = bit_vector1.set_value(bit_vector1.get_value(register_value)) 

88 value2 = bit_vector2.set_value(bit_vector2.get_value(register_value)) 

89 assert value0 | value1 | value2 == register_value 

90 

91 # Test numerical_interpretation 

92 field = BitVector( 

93 name="", 

94 base_index=2, 

95 description="", 

96 width=16, 

97 default_value="0" * 16, 

98 numerical_interpretation=SignedFixedPoint.from_bit_widths( 

99 integer_bit_width=8, fraction_bit_width=8 

100 ), 

101 ) 

102 assert field.set_value(-0.00390625) == 0b11_11111111_11111100 

103 

104 

105def test_min_and_max_value(): 

106 bit_vector = BitVector(name="", base_index=0, description="", width=4, default_value="0000") 

107 assert bit_vector.numerical_interpretation.min_value == 0 

108 assert bit_vector.numerical_interpretation.max_value == 15 

109 

110 bit_vector = BitVector( 

111 name="", 

112 base_index=0, 

113 description="", 

114 width=4, 

115 default_value="0000", 

116 numerical_interpretation=Signed(bit_width=4), 

117 ) 

118 assert bit_vector.numerical_interpretation.min_value == -8 

119 assert bit_vector.numerical_interpretation.max_value == 7 

120 

121 bit_vector = BitVector( 

122 name="", 

123 base_index=0, 

124 description="", 

125 width=4, 

126 default_value="0000", 

127 numerical_interpretation=UnsignedFixedPoint(1, -2), 

128 ) 

129 assert bit_vector.numerical_interpretation.min_value == 0 

130 assert bit_vector.numerical_interpretation.max_value == 3.75 

131 

132 bit_vector = BitVector( 

133 name="", 

134 base_index=0, 

135 description="", 

136 width=4, 

137 default_value="0000", 

138 numerical_interpretation=SignedFixedPoint(1, -2), 

139 ) 

140 assert bit_vector.numerical_interpretation.min_value == -2 

141 assert bit_vector.numerical_interpretation.max_value == 1.75 

142 

143 

144def test_repr(): 

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

146 assert "apa" in repr( 

147 BitVector(name="apa", base_index=0, description="", width=1, default_value="0") 

148 ) 

149 

150 # Different name 

151 assert repr( 

152 BitVector(name="apa", base_index=0, description="", width=1, default_value="0") 

153 ) != repr(BitVector(name="hest", base_index=0, description="", width=1, default_value="0")) 

154 

155 # Different base_index 

156 assert repr( 

157 BitVector(name="apa", base_index=0, description="", width=1, default_value="0") 

158 ) != repr(BitVector(name="apa", base_index=1, description="", width=1, default_value="0")) 

159 

160 # Different description 

161 assert repr( 

162 BitVector(name="apa", base_index=0, description="Blah", width=1, default_value="0") 

163 ) != repr(BitVector(name="apa", base_index=0, description="Gaah", width=1, default_value="0")) 

164 

165 # Different width 

166 assert repr( 

167 BitVector(name="apa", base_index=0, description="", width=1, default_value="1") 

168 ) != repr(BitVector(name="apa", base_index=0, description="", width=2, default_value="11")) 

169 

170 # Different default_value 

171 assert repr( 

172 BitVector(name="apa", base_index=0, description="", width=1, default_value="1") 

173 ) != repr(BitVector(name="apa", base_index=0, description="", width=1, default_value="0")) 

174 

175 # Same default_value but different representation in argument 

176 assert repr( 

177 BitVector(name="apa", base_index=0, description="", width=4, default_value="1101") 

178 ) == repr(BitVector(name="apa", base_index=0, description="", width=4, default_value=13)) 

179 

180 # Different numerical_interpretation 

181 field0 = BitVector( 

182 name="apa", 

183 base_index=0, 

184 description="", 

185 width=10, 

186 default_value="0" * 10, 

187 numerical_interpretation=UnsignedFixedPoint(max_bit_index=7, min_bit_index=-2), 

188 ) 

189 field1 = BitVector( 

190 name="apa", 

191 base_index=0, 

192 description="", 

193 width=10, 

194 default_value="0" * 10, 

195 numerical_interpretation=UnsignedFixedPoint(max_bit_index=7, min_bit_index=-2), 

196 ) 

197 field2 = BitVector( 

198 name="apa", 

199 base_index=0, 

200 description="", 

201 width=10, 

202 default_value="0" * 10, 

203 numerical_interpretation=UnsignedFixedPoint(max_bit_index=8, min_bit_index=-1), 

204 ) 

205 assert repr(field0) == repr(field1) != repr(field2) 

206 

207 

208def test_invalid_width(): 

209 with pytest.raises(TypeError) as exception_info: 

210 BitVector(name="foo", base_index=0, width="4", description="", default_value="0000") 

211 assert ( 

212 str(exception_info.value) 

213 == 'Bit vector "foo" should have integer value for "width". Got: "4".' 

214 ) 

215 

216 with pytest.raises(ValueError) as exception_info: 

217 BitVector(name="foo", base_index=0, width=33, description="", default_value="0") 

218 assert str(exception_info.value) == 'Invalid width for bit vector "foo". Got: "33".' 

219 

220 with pytest.raises(ValueError) as exception_info: 

221 BitVector(name="foo", base_index=0, width=0, description="", default_value="0") 

222 assert str(exception_info.value) == 'Invalid width for bit vector "foo". Got: "0".' 

223 

224 

225def test_invalid_default_value_should_raise_exception(): 

226 with pytest.raises(TypeError) as exception_info: 

227 BitVector(name="hest", base_index=0, description="", width=4, default_value=TypeError) 

228 assert str(exception_info.value) == ( 

229 'Bit vector "hest" should have string or numeric value for "default_value". ' 

230 "Got: \"<class 'TypeError'>\"." 

231 ) 

232 

233 

234def test_can_update_default_value(): 

235 bit_vector = BitVector(name="hest", base_index=0, description="", width=4, default_value="1111") 

236 assert bit_vector.default_value == "1111" 

237 

238 bit_vector.default_value = 3 

239 assert bit_vector.default_value == "0011" 

240 

241 

242def test_updating_to_invalid_default_value_should_raise_exception(): 

243 # Create with a valid default_value 

244 bit_vector = BitVector(name="hest", base_index=0, description="", width=4, default_value="1111") 

245 

246 # Update to an invalid value 

247 with pytest.raises(TypeError) as exception_info: 

248 bit_vector.default_value = TypeError 

249 assert str(exception_info.value) == ( 

250 'Bit vector "hest" should have string or numeric value for "default_value". ' 

251 "Got: \"<class 'TypeError'>\"." 

252 ) 

253 

254 

255def test_setting_non_binary_default_value_should_raise_exception(): 

256 # Update to an invalid value 

257 with pytest.raises(ValueError) as exception_info: 

258 BitVector(name="hest", base_index=0, description="", width=4, default_value="1211") 

259 assert str(exception_info.value) == ( 

260 'Bit vector "hest" invalid binary "default_value". Got: "1211".' 

261 ) 

262 

263 

264def test_default_value_uint(): 

265 bit_vector = BitVector(name="apa", base_index=0, description="", width=4, default_value="0000") 

266 assert bit_vector.default_value_uint == 0 

267 

268 bit_vector.default_value = "0010" 

269 assert bit_vector.default_value_uint == 2 

270 

271 bit_vector.default_value = "1001" 

272 assert bit_vector.default_value_uint == 9 

273 

274 

275def test_default_value_signed(): 

276 bit_vector = BitVector( 

277 name="apa", 

278 base_index=0, 

279 description="", 

280 width=4, 

281 default_value=-7, 

282 numerical_interpretation=Signed(bit_width=4), 

283 ) 

284 assert bit_vector.default_value == "1001" 

285 assert bit_vector.default_value_uint == 9 

286 

287 

288def test_default_value_ufixed(): 

289 bit_vector = BitVector( 

290 name="apa", 

291 base_index=0, 

292 description="", 

293 width=6, 

294 default_value=7.75, 

295 numerical_interpretation=SignedFixedPoint(max_bit_index=3, min_bit_index=-2), 

296 ) 

297 assert bit_vector.default_value == "011111" 

298 assert bit_vector.default_value_uint == 31 

299 

300 

301def test_default_value_sfixed(): 

302 bit_vector = BitVector( 

303 name="apa", 

304 base_index=0, 

305 description="", 

306 width=5, 

307 default_value=-7.5, 

308 numerical_interpretation=SignedFixedPoint(max_bit_index=3, min_bit_index=-1), 

309 ) 

310 assert bit_vector.default_value == "10001" 

311 assert bit_vector.default_value_uint == 17 

312 

313 

314def test_default_value_fixed_point_that_does_not_fit_perfectly_should_raise_exception(): 

315 bit_vector = BitVector( 

316 name="apa", 

317 base_index=0, 

318 description="", 

319 width=6, 

320 default_value=0, 

321 numerical_interpretation=SignedFixedPoint(max_bit_index=3, min_bit_index=-2), 

322 ) 

323 bit_vector.default_value = 7.75 

324 

325 with pytest.raises(ValueError) as exception_info: 

326 bit_vector.default_value = 7.76 

327 assert str(exception_info.value) == ( 

328 'Bit vector "apa" should have "default_value" that fits in 6 SignedFixedPoint bits. ' 

329 'Got: "7.76".' 

330 ) 

331 

332 

333def test_default_value_float_value_to_integer_should_raise_exception(): 

334 with pytest.raises(ValueError) as exception_info: 

335 BitVector(name="apa", base_index=0, description="", width=6, default_value=3.5) 

336 assert str(exception_info.value) == ( 

337 'Bit vector "apa" should have "default_value" that fits in 6 Unsigned bits. Got: "3.5".' 

338 ) 

339 

340 

341def test_numeric_interpretation(): 

342 bit_vector = BitVector(name="", base_index=5, description="", width=4, default_value="1111") 

343 assert isinstance(bit_vector.numerical_interpretation, Unsigned) 

344 assert bit_vector.numerical_interpretation.bit_width == 4 

345 

346 numerical_interpretation = Signed(bit_width=10) 

347 bit_vector = BitVector( 

348 name="", 

349 base_index=0, 

350 description="", 

351 width=10, 

352 default_value="1111111111", 

353 numerical_interpretation=numerical_interpretation, 

354 ) 

355 assert bit_vector.numerical_interpretation is numerical_interpretation 

356 

357 

358def test_invalid_numerical_interpretation_width_should_raise_exception(): 

359 def test(numerical_interpretation): 

360 with pytest.raises(ValueError) as exception_info: 

361 BitVector( 

362 name="apa", 

363 base_index=0, 

364 description="", 

365 width=4, 

366 default_value="1111", 

367 numerical_interpretation=numerical_interpretation, 

368 ) 

369 

370 bit_width = ( 

371 numerical_interpretation.integer_bit_width + numerical_interpretation.fraction_bit_width 

372 ) 

373 expected = ( 

374 f'Inconsistent width for bit vector "apa". ' 

375 f'Field is "4" bits, numerical interpretation specification is "{bit_width}".' 

376 ) 

377 assert str(exception_info.value) == expected 

378 

379 test(SignedFixedPoint(max_bit_index=7, min_bit_index=0)) 

380 test(UnsignedFixedPoint(max_bit_index=9, min_bit_index=-3)) 

381 

382 test(SignedFixedPoint(max_bit_index=5, min_bit_index=0)) 

383 test(UnsignedFixedPoint(max_bit_index=3, min_bit_index=-3))