/ test / func_blocks / fu / fpu / test_float_to_int.py
test_float_to_int.py
  1  from coreblocks.func_blocks.fu.fpu.float_to_int import *
  2  from coreblocks.func_blocks.fu.fpu.fpu_common import FPUParams, RoundingModes, Errors
  3  from test.func_blocks.fu.fpu.fpu_test_common import ToFloatConverter, python_to_float
  4  from transactron.testing import *
  5  from amaranth import *
  6  import random
  7  import ctypes
  8  from dataclasses import dataclass
  9  
 10  # Few notes for later.
 11  # 1. Due to the precision of float some conditions for out of bound numbers
 12  # are impossible to fulfill/test due to lack of precision (both for 32 and 64 bit integers).
 13  # Add more tests later when more precise versions of floating point numbers are available
 14  # 2. Due to the fact that we are not using rounding module but separatly
 15  # compute if rounding is needed, it may be worth to test all rounding modes or
 16  # modify rounding module to return one bit of information signifying if rounding occured
 17  
 18  converter = ToFloatConverter(FPUParams(sig_width=24, exp_width=8))
 19  
 20  
 21  @dataclass
 22  class TCase:
 23      op: dict[str, int]
 24      signed: int
 25      result: int
 26      errors: int
 27  
 28  
 29  max_un_int = (2**64) - 1
 30  min_sig_int = 2**63
 31  max_sig_int = (2**63) - 1
 32  
 33  test_cases = [
 34      # Test 1: Zero
 35      TCase(
 36          converter.from_hex("00000000"),
 37          1,
 38          0,
 39          0,
 40      ),
 41      # Test 2: NaN
 42      TCase(
 43          converter.from_hex("FFC00000"),
 44          0,
 45          max_un_int,
 46          Errors.INVALID_OPERATION,
 47      ),
 48      # Test 3: -Inf
 49      TCase(
 50          converter.from_hex("FF800000"),
 51          1,
 52          min_sig_int,
 53          Errors.INVALID_OPERATION,
 54      ),
 55      # Test 4: +Inf
 56      TCase(
 57          converter.from_hex("7F800000"),
 58          1,
 59          max_sig_int,
 60          Errors.INVALID_OPERATION,
 61      ),
 62      # Test 4: Rounding
 63      TCase(
 64          converter.from_hex("3FFFFFFF"),
 65          1,
 66          2,
 67          Errors.INEXACT,
 68      ),
 69      # Test 5: Out of bound negative
 70      TCase(
 71          converter.from_hex("DF000001"),
 72          1,
 73          min_sig_int,
 74          Errors.INVALID_OPERATION,
 75      ),
 76      # Test 6: Out of bound positive
 77      TCase(
 78          converter.from_hex("5F000000"),
 79          1,
 80          max_sig_int,
 81          Errors.INVALID_OPERATION,
 82      ),
 83      # Test 7: Mag less than one, out of bound
 84      TCase(
 85          converter.from_hex("bf7fffff"),
 86          0,
 87          0,
 88          Errors.INVALID_OPERATION,
 89      ),
 90  ]
 91  
 92  
 93  class TestFTI(TestCaseWithSimulator):
 94  
 95      def test_manual(self):
 96          params = FPUParams(sig_width=24, exp_width=8)
 97          fti = SimpleTestCircuit(FloatToIntModule(fpu_params=params, int_width=64))
 98  
 99          async def fti_ec_test(sim: TestbenchContext):
100              input_dict = {}
101              for tc in test_cases:
102                  input_dict["op"] = tc.op
103                  input_dict["signed"] = tc.signed
104                  input_dict["rounding_mode"] = RoundingModes.ROUND_NEAREST_EVEN
105  
106                  resp = await fti.fti_request.call(sim, input_dict)
107                  assert tc.result == resp["result"]
108                  assert tc.errors == resp["errors"]
109  
110          async def fti_python_test(sim: TestbenchContext):
111              seed = 42
112              random.seed(seed)
113              test_runs = 20
114  
115              for i in range(test_runs):
116  
117                  input_dict = {}
118                  op_sig = random.uniform(-(2 ** (63)), 2 ** (63) - 1)
119                  op_unsig = random.uniform(0, 2 ** (63))
120  
121                  fl_sig = python_to_float(op_sig)
122                  fl_unsig = python_to_float(op_unsig)
123  
124                  expected_value_sig = int(fl_sig)
125                  expected_value_unsig = int(fl_unsig)
126  
127                  input_sig = converter.from_float(op_sig)
128                  input_unsig = converter.from_float(op_unsig)
129  
130                  input_dict["op"] = input_sig
131                  input_dict["signed"] = 1
132                  input_dict["rounding_mode"] = RoundingModes.ROUND_NEAREST_EVEN
133  
134                  resp = await fti.fti_request.call(sim, input_dict)
135  
136                  resp_signed = ctypes.c_int64(resp["result"]).value
137                  assert expected_value_sig == resp_signed
138  
139                  input_dict["op"] = input_unsig
140                  input_dict["signed"] = 0
141                  input_dict["rounding_mode"] = RoundingModes.ROUND_NEAREST_EVEN
142  
143                  resp = await fti.fti_request.call(sim, input_dict)
144                  assert expected_value_unsig == resp["result"]
145  
146          async def test_process(sim: TestbenchContext):
147              await fti_python_test(sim)
148              await fti_ec_test(sim)
149  
150          with self.run_simulation(fti) as sim:
151              sim.add_testbench(test_process)