/ test / func_blocks / fu / fpu / fpu_test_common.py
fpu_test_common.py
  1  from coreblocks.func_blocks.fu.fpu.fpu_common import FPUCommonValues, FPUParams, RoundingModes
  2  from transactron.testing import *
  3  from enum import Enum
  4  from contextlib import contextmanager
  5  import struct
  6  import ctypes
  7  
  8  libm = ctypes.CDLL("libm.so.6")
  9  
 10  
 11  @contextmanager
 12  def python_float_tester():
 13      old_rm = libm.fegetround()
 14      try:
 15          yield old_rm
 16      finally:
 17          libm.fesetround(old_rm)
 18  
 19  
 20  class FPUTester:
 21      def __init__(self, params: FPUParams):
 22          self.params = params
 23          self.converter = ToFloatConverter(params)
 24  
 25      def __compare_results__(self, lhs, rhs):
 26          assert lhs["sign"] == rhs["sign"]
 27          assert lhs["exp"] == rhs["exp"]
 28          assert lhs["sig"] == rhs["sig"]
 29  
 30      async def run_test_set(self, cases, result, common_input, sim: TestbenchContext, request_adapter: TestbenchIO):
 31          assert len(cases) == len(result)
 32          for num, case in enumerate(cases):
 33              input_dict = {}
 34              for key, value in common_input.items():
 35                  input_dict[key] = value
 36              input_dict["op_1"] = self.converter.from_hex(case[0])
 37              input_dict["op_2"] = self.converter.from_hex(case[1])
 38              resp = await request_adapter.call(sim, input_dict)
 39              self.__compare_results__(resp, self.converter.from_hex(result[num][0]))
 40  
 41              assert resp["errors"] == int(result[num][1], 16)
 42  
 43  
 44  def python_to_float(p_float: float) -> float:
 45      try:
 46          return struct.unpack("f", struct.pack("f", p_float))[0]
 47      except OverflowError:
 48          if p_float > 3.4028235e38:
 49              return float("inf")
 50          elif p_float < -3.4028235e38:
 51              return float("-inf")
 52          else:
 53              raise
 54  
 55  
 56  class ToFloatConverter:
 57      def __init__(self, params: FPUParams):
 58          self.params = params
 59          # Width of the entire floating point number.
 60          # 1 is subtracted from sig_width because in memory significand is one bit
 61          # shorter than specified. This bit is encoded by exponent.
 62          sign_width = 1
 63          self.all_width = self.params.sig_width - 1 + self.params.exp_width + sign_width
 64          self.sig_width = self.params.sig_width - 1
 65          self.exp_mask = (2**self.params.exp_width) - 1
 66          self.sig_mask = (2 ** (self.params.sig_width - 1)) - 1
 67          self.implicit_one = 1 << (self.params.sig_width - 1)
 68          self.cv = FPUCommonValues(self.params)
 69  
 70      def from_float(self, fl):
 71          fl_hex = hex(struct.unpack("<I", struct.pack("<f", fl))[0])
 72          return self.from_hex(fl_hex)
 73  
 74      def from_hex(self, hex_float):
 75          number = int(hex_float, 16)
 76          exp = (number >> self.sig_width) & self.exp_mask
 77          sig = number & self.sig_mask
 78          if exp != 0:
 79              sig = sig | self.implicit_one
 80          return {
 81              "sign": number >> (self.all_width - 1),
 82              "exp": exp,
 83              "sig": sig,
 84              "is_inf": ((exp == self.cv.max_exp) & ((sig & (~self.implicit_one)) == 0)),
 85              "is_nan": ((exp == self.cv.max_exp) & ((sig & (~self.implicit_one)) != 0)),
 86              "is_zero": ((exp == 0) & (sig == 0)),
 87          }
 88  
 89  
 90  class FenvRm(Enum):
 91      FE_TONEAREST = 0x0000
 92      FE_DOWNWARD = 0x400
 93      FE_UPWARD = 0x800
 94      FE_TOWARDZERO = 0xC00
 95  
 96  
 97  def fenv_rm_to_fpu_rm(fenv_rm):
 98      match fenv_rm:
 99          case FenvRm.FE_TONEAREST:
100              return RoundingModes.ROUND_NEAREST_EVEN
101          case FenvRm.FE_DOWNWARD:
102              return RoundingModes.ROUND_DOWN
103          case FenvRm.FE_UPWARD:
104              return RoundingModes.ROUND_UP
105          case FenvRm.FE_TOWARDZERO:
106              return RoundingModes.ROUND_ZERO