test_fpu_error.py
1 from coreblocks.func_blocks.fu.fpu.fpu_error_module import * 2 from coreblocks.func_blocks.fu.fpu.fpu_common import ( 3 RoundingModes, 4 FPUParams, 5 Errors, 6 ) 7 from transactron import TModule 8 from transactron.lib import AdapterTrans 9 from parameterized import parameterized 10 from transactron.testing import * 11 from amaranth import * 12 13 14 class TestFPUError(TestCaseWithSimulator): 15 class FPUErrorModule(Elaboratable): 16 def __init__(self, params: FPUParams): 17 self.params = params 18 19 def elaborate(self, platform): 20 m = TModule() 21 m.submodules.fpue = fpue = self.fpu_error_module = FPUErrorModule(fpu_params=self.params) 22 m.submodules.error_checking = self.error_checking_request_adapter = TestbenchIO( 23 AdapterTrans.create(fpue.error_checking_request) 24 ) 25 return m 26 27 class HelpValues: 28 def __init__(self, params: FPUParams): 29 self.params = params 30 self.implicit_bit = 2 ** (self.params.sig_width - 1) 31 self.max_exp = (2**self.params.exp_width) - 1 32 self.max_norm_exp = (2**self.params.exp_width) - 2 33 self.not_max_norm_exp = (2**self.params.exp_width) - 3 34 self.max_sig = (2**params.sig_width) - 1 35 self.not_max_norm_sig = 1 << (self.params.sig_width - 1) | 1 36 self.not_max_norm_even_sig = 1 << (self.params.sig_width - 1) 37 self.sub_norm_sig = 3 38 self.min_norm_sig = 1 << (self.params.sig_width - 1) 39 self.max_sub_norm_sig = (2 ** (self.params.sig_width - 1)) - 1 40 self.qnan = 3 << (self.params.sig_width - 2) | 1 41 42 params = FPUParams(sig_width=24, exp_width=8) 43 help_values = HelpValues(params) 44 45 @parameterized.expand([(params, help_values)]) 46 def test_special_cases(self, params: FPUParams, help_values: HelpValues): 47 fpue = TestFPUError.FPUErrorModule(params) 48 49 async def other_cases_test(sim: TestbenchContext): 50 test_cases = [ 51 # No errors 52 { 53 "sign": 0, 54 "sig": help_values.not_max_norm_even_sig, 55 "exp": help_values.not_max_norm_exp, 56 "inexact": 0, 57 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 58 "invalid_operation": 0, 59 "division_by_zero": 0, 60 "input_inf": 0, 61 }, 62 # inexact 63 { 64 "sign": 0, 65 "sig": help_values.not_max_norm_even_sig, 66 "exp": help_values.not_max_norm_exp, 67 "inexact": 1, 68 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 69 "invalid_operation": 0, 70 "division_by_zero": 0, 71 "input_inf": 0, 72 }, 73 # underflow 74 { 75 "sign": 0, 76 "sig": help_values.sub_norm_sig, 77 "exp": 0, 78 "inexact": 1, 79 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 80 "invalid_operation": 0, 81 "division_by_zero": 0, 82 "input_inf": 0, 83 }, 84 # invalid operation 85 { 86 "sign": 0, 87 "sig": help_values.qnan, 88 "exp": help_values.max_exp, 89 "inexact": 1, 90 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 91 "invalid_operation": 1, 92 "division_by_zero": 0, 93 "input_inf": 0, 94 }, 95 # division by zero 96 { 97 "sign": 0, 98 "sig": help_values.implicit_bit, 99 "exp": help_values.max_exp, 100 "inexact": 1, 101 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 102 "invalid_operation": 0, 103 "division_by_zero": 1, 104 "input_inf": 0, 105 }, 106 # overflow but no round and sticky bits 107 { 108 "sign": 0, 109 "sig": help_values.implicit_bit, 110 "exp": help_values.max_exp, 111 "inexact": 0, 112 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 113 "invalid_operation": 0, 114 "division_by_zero": 0, 115 "input_inf": 0, 116 }, 117 # tininess but no underflow 118 { 119 "sign": 0, 120 "sig": help_values.sub_norm_sig, 121 "exp": 0, 122 "inexact": 0, 123 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 124 "invalid_operation": 0, 125 "division_by_zero": 0, 126 "input_inf": 0, 127 }, 128 # one of inputs was qnan 129 { 130 "sign": 0, 131 "sig": help_values.qnan, 132 "exp": help_values.max_exp, 133 "inexact": 1, 134 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 135 "invalid_operation": 0, 136 "division_by_zero": 0, 137 "input_inf": 0, 138 }, 139 # one of inputs was inf 140 { 141 "sign": 1, 142 "sig": help_values.implicit_bit, 143 "exp": help_values.max_exp, 144 "inexact": 1, 145 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 146 "invalid_operation": 0, 147 "division_by_zero": 0, 148 "input_inf": 1, 149 }, 150 # subnormal number become normalized after rounding 151 { 152 "sign": 1, 153 "sig": help_values.min_norm_sig, 154 "exp": 0, 155 "inexact": 1, 156 "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, 157 "invalid_operation": 0, 158 "division_by_zero": 0, 159 "input_inf": 0, 160 }, 161 ] 162 163 expected_results = [ 164 # No errors 165 {"sign": 0, "sig": help_values.not_max_norm_even_sig, "exp": help_values.not_max_norm_exp, "errors": 0}, 166 # inexact 167 { 168 "sign": 0, 169 "sig": help_values.not_max_norm_even_sig, 170 "exp": help_values.not_max_norm_exp, 171 "errors": Errors.INEXACT, 172 }, 173 # underflow 174 {"sign": 0, "sig": help_values.sub_norm_sig, "exp": 0, "errors": Errors.UNDERFLOW | Errors.INEXACT}, 175 # invalid operation 176 {"sign": 0, "sig": help_values.qnan, "exp": help_values.max_exp, "errors": Errors.INVALID_OPERATION}, 177 # division by zero 178 { 179 "sign": 0, 180 "sig": help_values.implicit_bit, 181 "exp": help_values.max_exp, 182 "errors": Errors.DIVISION_BY_ZERO, 183 }, 184 # overflow but no round and sticky bits 185 { 186 "sign": 0, 187 "sig": help_values.implicit_bit, 188 "exp": help_values.max_exp, 189 "errors": Errors.INEXACT | Errors.OVERFLOW, 190 }, 191 # tininess but no underflow 192 {"sign": 0, "sig": help_values.sub_norm_sig, "exp": 0, "errors": 0}, 193 # one of inputs was qnan 194 {"sign": 0, "sig": help_values.qnan, "exp": help_values.max_exp, "errors": 0}, 195 # one of inputs was inf 196 {"sign": 1, "sig": help_values.implicit_bit, "exp": help_values.max_exp, "errors": 0}, 197 # subnormal number become normalized after rounding 198 {"sign": 1, "sig": help_values.min_norm_sig, "exp": 1, "errors": Errors.INEXACT}, 199 ] 200 for i in range(len(test_cases)): 201 resp = await fpue.error_checking_request_adapter.call(sim, test_cases[i]) 202 assert resp.sign == expected_results[i]["sign"] 203 assert resp.exp == expected_results[i]["exp"] 204 assert resp.sig == expected_results[i]["sig"] 205 assert resp.errors == expected_results[i]["errors"] 206 207 async def test_process(sim: TestbenchContext): 208 await other_cases_test(sim) 209 210 with self.run_simulation(fpue) as sim: 211 sim.add_testbench(test_process) 212 213 @parameterized.expand( 214 [ 215 ( 216 params, 217 help_values, 218 RoundingModes.ROUND_NEAREST_EVEN, 219 help_values.implicit_bit, 220 help_values.max_exp, 221 help_values.implicit_bit, 222 help_values.max_exp, 223 ), 224 ( 225 params, 226 help_values, 227 RoundingModes.ROUND_NEAREST_AWAY, 228 help_values.implicit_bit, 229 help_values.max_exp, 230 help_values.implicit_bit, 231 help_values.max_exp, 232 ), 233 ( 234 params, 235 help_values, 236 RoundingModes.ROUND_UP, 237 help_values.implicit_bit, 238 help_values.max_exp, 239 help_values.max_sig, 240 help_values.max_norm_exp, 241 ), 242 ( 243 params, 244 help_values, 245 RoundingModes.ROUND_DOWN, 246 help_values.max_sig, 247 help_values.max_norm_exp, 248 help_values.implicit_bit, 249 help_values.max_exp, 250 ), 251 ( 252 params, 253 help_values, 254 RoundingModes.ROUND_ZERO, 255 help_values.max_sig, 256 help_values.max_norm_exp, 257 help_values.max_sig, 258 help_values.max_norm_exp, 259 ), 260 ] 261 ) 262 def test_rounding( 263 self, 264 params: FPUParams, 265 help_values: HelpValues, 266 rm: RoundingModes, 267 plus_overflow_sig: int, 268 plus_overflow_exp: int, 269 minus_overflow_sig: int, 270 minus_overflow_exp: int, 271 ): 272 fpue = TestFPUError.FPUErrorModule(params) 273 274 async def one_rounding_mode_test(sim: TestbenchContext): 275 test_cases = [ 276 # overflow detection 277 { 278 "sign": 0, 279 "sig": help_values.implicit_bit, 280 "exp": help_values.max_exp, 281 "rounding_mode": rm, 282 "inexact": 0, 283 "invalid_operation": 0, 284 "division_by_zero": 0, 285 "input_inf": 0, 286 }, 287 { 288 "sign": 1, 289 "sig": help_values.implicit_bit, 290 "exp": help_values.max_exp, 291 "rounding_mode": rm, 292 "inexact": 0, 293 "invalid_operation": 0, 294 "division_by_zero": 0, 295 "input_inf": 0, 296 }, 297 ] 298 expected_results = [ 299 # overflow detection 300 { 301 "sign": 0, 302 "sig": plus_overflow_sig, 303 "exp": plus_overflow_exp, 304 "errors": Errors.INEXACT | Errors.OVERFLOW, 305 }, 306 { 307 "sign": 1, 308 "sig": minus_overflow_sig, 309 "exp": minus_overflow_exp, 310 "errors": Errors.INEXACT | Errors.OVERFLOW, 311 }, 312 ] 313 314 for i in range(len(test_cases)): 315 resp = await fpue.error_checking_request_adapter.call(sim, test_cases[i]) 316 assert resp["sign"] == expected_results[i]["sign"] 317 assert resp["exp"] == expected_results[i]["exp"] 318 assert resp["sig"] == expected_results[i]["sig"] 319 assert resp["errors"] == expected_results[i]["errors"] 320 321 async def test_process(sim: TestbenchContext): 322 await one_rounding_mode_test(sim) 323 324 with self.run_simulation(fpue) as sim: 325 sim.add_testbench(test_process)