diff --git a/coreblocks/func_blocks/fu/fpu/__init__.py b/coreblocks/func_blocks/fu/fpu/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/coreblocks/func_blocks/fu/fpu/fpu_common.py b/coreblocks/func_blocks/fu/fpu/fpu_common.py new file mode 100644 index 000000000..14ad02739 --- /dev/null +++ b/coreblocks/func_blocks/fu/fpu/fpu_common.py @@ -0,0 +1,38 @@ +from amaranth.lib import enum + + +class RoundingModes(enum.Enum): + ROUND_UP = 3 + ROUND_DOWN = 2 + ROUND_ZERO = 1 + ROUND_NEAREST_EVEN = 0 + ROUND_NEAREST_AWAY = 4 + + +class Errors(enum.IntFlag): + INVALID_OPERATION = enum.auto() + DIVISION_BY_ZERO = enum.auto() + OVERFLOW = enum.auto() + UNDERFLOW = enum.auto() + INEXACT = enum.auto() + + +class FPUParams: + """FPU parameters + + Parameters + ---------- + sig_width: int + Width of significand, including implicit bit + exp_width: int + Width of exponent + """ + + def __init__( + self, + *, + sig_width: int = 24, + exp_width: int = 8, + ): + self.sig_width = sig_width + self.exp_width = exp_width diff --git a/coreblocks/func_blocks/fu/fpu/fpu_error_module.py b/coreblocks/func_blocks/fu/fpu/fpu_error_module.py new file mode 100644 index 000000000..5759f34f5 --- /dev/null +++ b/coreblocks/func_blocks/fu/fpu/fpu_error_module.py @@ -0,0 +1,176 @@ +from amaranth import * +from transactron import TModule, Method, def_method +from coreblocks.func_blocks.fu.fpu.fpu_common import ( + RoundingModes, + FPUParams, + Errors, +) + + +class FPUErrorMethodLayout: + """FPU error checking module layouts for methods + + Parameters + ---------- + fpu_params: FPUParams + FPU parameters + """ + + def __init__(self, *, fpu_params: FPUParams): + """ + input_inf is a flag that comes from previous stage. + Its purpose is to indicate that the infinity on input + is a result of infinity arithmetic and not a result of overflow + """ + self.error_in_layout = [ + ("sign", 1), + ("sig", fpu_params.sig_width), + ("exp", fpu_params.exp_width), + ("rounding_mode", RoundingModes), + ("inexact", 1), + ("invalid_operation", 1), + ("division_by_zero", 1), + ("input_inf", 1), + ] + self.error_out_layout = [ + ("sign", 1), + ("sig", fpu_params.sig_width), + ("exp", fpu_params.exp_width), + ("errors", Errors), + ] + + +class FPUErrorModule(Elaboratable): + """FPU error checking module + + Parameters + ---------- + fpu_params: FPUParams + FPU rounding module parameters + + Attributes + ---------- + error_checking_request: Method + Transactional method for initiating error checking of a floating point number. + Takes 'error_in_layout' as argument + Returns final number and errors as 'error_out_layout' + """ + + def __init__(self, *, fpu_params: FPUParams): + + self.fpu_errors_params = fpu_params + self.method_layouts = FPUErrorMethodLayout(fpu_params=self.fpu_errors_params) + self.error_checking_request = Method( + i=self.method_layouts.error_in_layout, + o=self.method_layouts.error_out_layout, + ) + + def elaborate(self, platform): + m = TModule() + + max_exp = C( + 2 ** (self.fpu_errors_params.exp_width) - 1, + unsigned(self.fpu_errors_params.exp_width), + ) + max_normal_exp = C( + 2 ** (self.fpu_errors_params.exp_width) - 2, + unsigned(self.fpu_errors_params.exp_width), + ) + max_sig = C( + 2 ** (self.fpu_errors_params.sig_width) - 1, + unsigned(self.fpu_errors_params.sig_width), + ) + + overflow = Signal() + underflow = Signal() + inexact = Signal() + tininess = Signal() + + final_exp = Signal(self.fpu_errors_params.exp_width) + final_sig = Signal(self.fpu_errors_params.sig_width) + final_sign = Signal() + final_errors = Signal(5) + + @def_method(m, self.error_checking_request) + def _(arg): + is_nan = arg.invalid_operation | ((arg.exp == max_exp) & (arg.sig.any())) + is_inf = arg.division_by_zero | arg.input_inf + input_not_special = ~(is_nan) & ~(is_inf) + m.d.av_comb += overflow.eq(input_not_special & (arg.exp == max_exp)) + m.d.av_comb += tininess.eq((arg.exp == 0) & (~arg.sig[-1])) + m.d.av_comb += inexact.eq(overflow | (input_not_special & arg.inexact)) + m.d.av_comb += underflow.eq(tininess & inexact) + + with m.If(is_nan | is_inf): + + m.d.av_comb += final_exp.eq(arg.exp) + m.d.av_comb += final_sig.eq(arg.sig) + m.d.av_comb += final_sign.eq(arg.sign) + + with m.Elif(overflow): + + with m.Switch(arg.rounding_mode): + with m.Case(RoundingModes.ROUND_NEAREST_AWAY, RoundingModes.ROUND_NEAREST_EVEN): + + m.d.av_comb += final_exp.eq(max_exp) + m.d.av_comb += final_sig.eq(0) + m.d.av_comb += final_sign.eq(arg.sign) + + with m.Case(RoundingModes.ROUND_ZERO): + + m.d.av_comb += final_exp.eq(max_normal_exp) + m.d.av_comb += final_sig.eq(max_sig) + m.d.av_comb += final_sign.eq(arg.sign) + + with m.Case(RoundingModes.ROUND_DOWN): + + with m.If(arg.sign): + + m.d.av_comb += final_exp.eq(max_exp) + m.d.av_comb += final_sig.eq(0) + m.d.av_comb += final_sign.eq(arg.sign) + + with m.Else(): + + m.d.av_comb += final_exp.eq(max_normal_exp) + m.d.av_comb += final_sig.eq(max_sig) + m.d.av_comb += final_sign.eq(arg.sign) + + with m.Case(RoundingModes.ROUND_UP): + + with m.If(arg.sign): + + m.d.av_comb += final_exp.eq(max_normal_exp) + m.d.av_comb += final_sig.eq(max_sig) + m.d.av_comb += final_sign.eq(arg.sign) + + with m.Else(): + + m.d.av_comb += final_exp.eq(max_exp) + m.d.av_comb += final_sig.eq(0) + m.d.av_comb += final_sign.eq(arg.sign) + + with m.Else(): + with m.If((arg.exp == 0) & (arg.sig[-1] == 1)): + m.d.av_comb += final_exp.eq(1) + with m.Else(): + m.d.av_comb += final_exp.eq(arg.exp) + m.d.av_comb += final_sig.eq(arg.sig) + m.d.av_comb += final_sign.eq(arg.sign) + + m.d.av_comb += final_errors.eq( + Mux(arg.invalid_operation, Errors.INVALID_OPERATION, 0) + | Mux(arg.division_by_zero, Errors.DIVISION_BY_ZERO, 0) + | Mux(overflow, Errors.OVERFLOW, 0) + | Mux(underflow, Errors.UNDERFLOW, 0) + | Mux(inexact, Errors.INEXACT, 0) + ) + + return { + "exp": final_exp, + "sig": final_sig, + "sign": final_sign, + "errors": final_errors, + } + + return m diff --git a/coreblocks/func_blocks/fu/fpu/fpu_rounding_module.py b/coreblocks/func_blocks/fu/fpu/fpu_rounding_module.py new file mode 100644 index 000000000..267d8557d --- /dev/null +++ b/coreblocks/func_blocks/fu/fpu/fpu_rounding_module.py @@ -0,0 +1,117 @@ +from amaranth import * +from transactron import TModule, Method, def_method +from coreblocks.func_blocks.fu.fpu.fpu_common import ( + RoundingModes, + FPUParams, +) + + +class FPURoudningMethodLayout: + """FPU Rounding module layouts for methods + + Parameters + ---------- + fpu_params: FPUParams + FPU parameters + """ + + def __init__(self, *, fpu_params: FPUParams): + self.rounding_in_layout = [ + ("sign", 1), + ("sig", fpu_params.sig_width), + ("exp", fpu_params.exp_width), + ("round_bit", 1), + ("sticky_bit", 1), + ("rounding_mode", RoundingModes), + ] + self.rounding_out_layout = [ + ("sig", fpu_params.sig_width), + ("exp", fpu_params.exp_width), + ("inexact", 1), + ] + + +class FPURounding(Elaboratable): + """FPU Rounding module + + Parameters + ---------- + fpu_params: FPUParams + FPU parameters + + Attributes + ---------- + rounding_request: Method + Transactional method for initiating rounding of a floating point number. + Takes 'rounding_in_layout' as argument + Returns rounded number and errors as 'rounding_out_layout' + """ + + def __init__(self, *, fpu_params: FPUParams): + + self.fpu_rounding_params = fpu_params + self.method_layouts = FPURoudningMethodLayout(fpu_params=self.fpu_rounding_params) + self.rounding_request = Method( + i=self.method_layouts.rounding_in_layout, + o=self.method_layouts.rounding_out_layout, + ) + + def elaborate(self, platform): + m = TModule() + + add_one = Signal() + inc_rtnte = Signal() + inc_rtnta = Signal() + inc_rtpi = Signal() + inc_rtmi = Signal() + + rounded_sig = Signal(self.fpu_rounding_params.sig_width + 1) + normalised_sig = Signal(self.fpu_rounding_params.sig_width) + rounded_exp = Signal(self.fpu_rounding_params.exp_width) + + final_round_bit = Signal() + final_sticky_bit = Signal() + + inexact = Signal() + + @def_method(m, self.rounding_request) + def _(arg): + + m.d.av_comb += inc_rtnte.eq( + (arg.rounding_mode == RoundingModes.ROUND_NEAREST_EVEN) + & (arg.round_bit & (arg.sticky_bit | arg.sig[0])) + ) + m.d.av_comb += inc_rtnta.eq((arg.rounding_mode == RoundingModes.ROUND_NEAREST_AWAY) & (arg.round_bit)) + m.d.av_comb += inc_rtpi.eq( + (arg.rounding_mode == RoundingModes.ROUND_UP) & (~arg.sign & (arg.round_bit | arg.sticky_bit)) + ) + m.d.av_comb += inc_rtmi.eq( + (arg.rounding_mode == RoundingModes.ROUND_DOWN) & (arg.sign & (arg.round_bit | arg.sticky_bit)) + ) + + m.d.av_comb += add_one.eq(inc_rtmi | inc_rtnta | inc_rtnte | inc_rtpi) + + m.d.av_comb += rounded_sig.eq(arg.sig + add_one) + + with m.If(rounded_sig[-1]): + + m.d.av_comb += normalised_sig.eq(rounded_sig >> 1) + m.d.av_comb += final_round_bit.eq(rounded_sig[0]) + m.d.av_comb += final_sticky_bit.eq(arg.round_bit | arg.sticky_bit) + m.d.av_comb += rounded_exp.eq(arg.exp + 1) + + with m.Else(): + m.d.av_comb += normalised_sig.eq(rounded_sig) + m.d.av_comb += final_round_bit.eq(arg.round_bit) + m.d.av_comb += final_sticky_bit.eq(arg.sticky_bit) + m.d.av_comb += rounded_exp.eq(arg.exp) + + m.d.av_comb += inexact.eq(final_round_bit | final_sticky_bit) + + return { + "exp": rounded_exp, + "sig": normalised_sig, + "inexact": inexact, + } + + return m diff --git a/test/func_blocks/fu/fpu/test_fpu_error.py b/test/func_blocks/fu/fpu/test_fpu_error.py new file mode 100644 index 000000000..6938bfd17 --- /dev/null +++ b/test/func_blocks/fu/fpu/test_fpu_error.py @@ -0,0 +1,305 @@ +from coreblocks.func_blocks.fu.fpu.fpu_error_module import * +from coreblocks.func_blocks.fu.fpu.fpu_common import ( + RoundingModes, + FPUParams, + Errors, +) +from transactron import TModule +from transactron.lib import AdapterTrans +from parameterized import parameterized +from transactron.testing import * +from amaranth import * + + +class TestFPUError(TestCaseWithSimulator): + class FPUErrorModule(Elaboratable): + def __init__(self, params: FPUParams): + self.params = params + + def elaborate(self, platform): + m = TModule() + m.submodules.fpue = fpue = self.fpu_error_module = FPUErrorModule(fpu_params=self.params) + m.submodules.error_checking = self.error_checking_request_adapter = TestbenchIO( + AdapterTrans(fpue.error_checking_request) + ) + return m + + class HelpValues: + def __init__(self, params: FPUParams): + self.params = params + self.max_exp = (2**self.params.exp_width) - 1 + self.max_norm_exp = (2**self.params.exp_width) - 2 + self.not_max_norm_exp = (2**self.params.exp_width) - 3 + self.max_sig = (2**params.sig_width) - 1 + self.not_max_norm_sig = 1 << (self.params.sig_width - 1) | 1 + self.not_max_norm_even_sig = 1 << (self.params.sig_width - 1) + self.sub_norm_sig = 3 + self.min_norm_sig = 1 << (self.params.sig_width - 1) + self.max_sub_norm_sig = (2 ** (self.params.sig_width - 1)) - 1 + self.qnan = 3 << (self.params.sig_width - 2) | 1 + + params = FPUParams(sig_width=24, exp_width=8) + help_values = HelpValues(params) + + @parameterized.expand([(params, help_values)]) + def test_special_cases(self, params: FPUParams, help_values: HelpValues): + fpue = TestFPUError.FPUErrorModule(params) + + def other_cases_test(): + test_cases = [ + # No errors + { + "sign": 0, + "sig": help_values.not_max_norm_even_sig, + "exp": help_values.not_max_norm_exp, + "inexact": 0, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 0, + }, + # inexact + { + "sign": 0, + "sig": help_values.not_max_norm_even_sig, + "exp": help_values.not_max_norm_exp, + "inexact": 1, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 0, + }, + # underflow + { + "sign": 0, + "sig": help_values.sub_norm_sig, + "exp": 0, + "inexact": 1, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 0, + }, + # invalid operation + { + "sign": 0, + "sig": help_values.qnan, + "exp": help_values.max_exp, + "inexact": 1, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 1, + "division_by_zero": 0, + "input_inf": 0, + }, + # division by zero + { + "sign": 0, + "sig": 0, + "exp": help_values.max_exp, + "inexact": 1, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 0, + "division_by_zero": 1, + "input_inf": 0, + }, + # overflow but no round and sticky bits + { + "sign": 0, + "sig": 0, + "exp": help_values.max_exp, + "inexact": 0, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 0, + }, + # tininess but no underflow + { + "sign": 0, + "sig": help_values.sub_norm_sig, + "exp": 0, + "inexact": 0, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 0, + }, + # one of inputs was qnan + { + "sign": 0, + "sig": help_values.qnan, + "exp": help_values.max_exp, + "inexact": 1, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 0, + }, + # one of inputs was inf + { + "sign": 1, + "sig": 0, + "exp": help_values.max_exp, + "inexact": 1, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 1, + }, + # subnormal number become normalized after rounding + { + "sign": 1, + "sig": help_values.min_norm_sig, + "exp": 0, + "inexact": 1, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 0, + }, + ] + + expected_results = [ + # No errors + {"sign": 0, "sig": help_values.not_max_norm_even_sig, "exp": help_values.not_max_norm_exp, "errors": 0}, + # inexact + { + "sign": 0, + "sig": help_values.not_max_norm_even_sig, + "exp": help_values.not_max_norm_exp, + "errors": Errors.INEXACT, + }, + # underflow + {"sign": 0, "sig": help_values.sub_norm_sig, "exp": 0, "errors": Errors.UNDERFLOW | Errors.INEXACT}, + # invalid operation + {"sign": 0, "sig": help_values.qnan, "exp": help_values.max_exp, "errors": Errors.INVALID_OPERATION}, + # division by zero + {"sign": 0, "sig": 0, "exp": help_values.max_exp, "errors": Errors.DIVISION_BY_ZERO}, + # overflow but no round and sticky bits + {"sign": 0, "sig": 0, "exp": help_values.max_exp, "errors": Errors.INEXACT | Errors.OVERFLOW}, + # tininess but no underflow + {"sign": 0, "sig": help_values.sub_norm_sig, "exp": 0, "errors": 0}, + # one of inputs was qnan + {"sign": 0, "sig": help_values.qnan, "exp": help_values.max_exp, "errors": 0}, + # one of inputs was inf + {"sign": 1, "sig": 0, "exp": help_values.max_exp, "errors": 0}, + # subnormal number become normalized after rounding + {"sign": 1, "sig": help_values.min_norm_sig, "exp": 1, "errors": Errors.INEXACT}, + ] + for i in range(len(test_cases)): + + resp = yield from fpue.error_checking_request_adapter.call(test_cases[i]) + assert resp["sign"] == expected_results[i]["sign"] + assert resp["exp"] == expected_results[i]["exp"] + assert resp["sig"] == expected_results[i]["sig"] + assert resp["errors"] == expected_results[i]["errors"] + + def test_process(): + yield from other_cases_test() + + with self.run_simulation(fpue) as sim: + sim.add_process(test_process) + + @parameterized.expand( + [ + ( + params, + help_values, + RoundingModes.ROUND_NEAREST_EVEN, + 0, + help_values.max_exp, + 0, + help_values.max_exp, + ), + ( + params, + help_values, + RoundingModes.ROUND_NEAREST_AWAY, + 0, + help_values.max_exp, + 0, + help_values.max_exp, + ), + ( + params, + help_values, + RoundingModes.ROUND_UP, + 0, + help_values.max_exp, + help_values.max_sig, + help_values.max_norm_exp, + ), + ( + params, + help_values, + RoundingModes.ROUND_DOWN, + help_values.max_sig, + help_values.max_norm_exp, + 0, + help_values.max_exp, + ), + ( + params, + help_values, + RoundingModes.ROUND_ZERO, + help_values.max_sig, + help_values.max_norm_exp, + help_values.max_sig, + help_values.max_norm_exp, + ), + ] + ) + def test_rounding( + self, + params: FPUParams, + help_values: HelpValues, + rm: RoundingModes, + plus_overflow_sig: int, + plus_overflow_exp: int, + minus_overflow_sig: int, + minus_overflow_exp: int, + ): + fpue = TestFPUError.FPUErrorModule(params) + + def one_rounding_mode_test(): + test_cases = [ + # overflow detection + { + "sign": 0, + "sig": 0, + "exp": help_values.max_exp, + "rounding_mode": rm, + "inexact": 0, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 0, + }, + { + "sign": 1, + "sig": 0, + "exp": help_values.max_exp, + "rounding_mode": rm, + "inexact": 0, + "invalid_operation": 0, + "division_by_zero": 0, + "input_inf": 0, + }, + ] + expected_results = [ + # overflow detection + {"sign": 0, "sig": plus_overflow_sig, "exp": plus_overflow_exp, "errors": 20}, + {"sign": 1, "sig": minus_overflow_sig, "exp": minus_overflow_exp, "errors": 20}, + ] + + for i in range(len(test_cases)): + resp = yield from fpue.error_checking_request_adapter.call(test_cases[i]) + assert resp["sign"] == expected_results[i]["sign"] + assert resp["exp"] == expected_results[i]["exp"] + assert resp["sig"] == expected_results[i]["sig"] + assert resp["errors"] == expected_results[i]["errors"] + + def test_process(): + yield from one_rounding_mode_test() + + with self.run_simulation(fpue) as sim: + sim.add_process(test_process) diff --git a/test/func_blocks/fu/fpu/test_fpu_rounding.py b/test/func_blocks/fu/fpu/test_fpu_rounding.py new file mode 100644 index 000000000..0b1e40865 --- /dev/null +++ b/test/func_blocks/fu/fpu/test_fpu_rounding.py @@ -0,0 +1,276 @@ +from coreblocks.func_blocks.fu.fpu.fpu_rounding_module import * +from coreblocks.func_blocks.fu.fpu.fpu_common import ( + RoundingModes, + FPUParams, +) +from transactron import TModule +from transactron.lib import AdapterTrans +from parameterized import parameterized +from transactron.testing import * +from amaranth import * + + +class TestFPURounding(TestCaseWithSimulator): + class FPURoundingModule(Elaboratable): + def __init__(self, params: FPUParams): + self.params = params + + def elaborate(self, platform): + m = TModule() + m.submodules.fpur = fpur = self.fpu_rounding = FPURounding(fpu_params=self.params) + m.submodules.rounding = self.rounding_request_adapter = TestbenchIO(AdapterTrans(fpur.rounding_request)) + return m + + class HelpValues: + def __init__(self, params: FPUParams): + self.params = params + self.max_exp = (2**self.params.exp_width) - 1 + self.max_norm_exp = (2**self.params.exp_width) - 2 + self.not_max_norm_exp = (2**self.params.exp_width) - 3 + self.max_sig = (2**params.sig_width) - 1 + self.not_max_norm_sig = 1 << (self.params.sig_width - 1) | 1 + self.not_max_norm_even_sig = 1 << (self.params.sig_width - 1) + self.sub_norm_sig = 3 + self.max_sub_norm_sig = (2 ** (self.params.sig_width - 1)) - 1 + self.qnan = 3 << (self.params.sig_width - 2) | 1 + + params = FPUParams(sig_width=24, exp_width=8) + help_values = HelpValues(params) + + tie_to_even_inc_array = [ + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + ] + tie_to_away_inc_array = [0, 1, 0, 1, 0, 1, 0, 1] + round_up_inc_array = [0, 1, 1, 1, 0, 0, 0, 0] + round_down_inc_array = [0, 0, 0, 0, 0, 1, 1, 1] + round_zero_inc_array = [0, 0, 0, 0, 0, 0, 0, 0] + + @parameterized.expand( + [ + ( + params, + help_values, + RoundingModes.ROUND_NEAREST_EVEN, + tie_to_away_inc_array, + ), + ( + params, + help_values, + RoundingModes.ROUND_NEAREST_AWAY, + tie_to_away_inc_array, + ), + ( + params, + help_values, + RoundingModes.ROUND_UP, + round_up_inc_array, + ), + ( + params, + help_values, + RoundingModes.ROUND_DOWN, + round_down_inc_array, + ), + ( + params, + help_values, + RoundingModes.ROUND_ZERO, + round_zero_inc_array, + ), + ] + ) + def test_rounding( + self, + params: FPUParams, + help_values: HelpValues, + rm: RoundingModes, + inc_arr: list, + ): + fpurt = TestFPURounding.FPURoundingModule(params) + + def one_rounding_mode_test(): + test_cases = [ + # carry after increment + { + "sign": 0 if rm != RoundingModes.ROUND_DOWN else 1, + "sig": help_values.max_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 1, + "sticky_bit": 1, + "rounding_mode": rm, + }, + # no overflow 00 + { + "sign": 0, + "sig": help_values.not_max_norm_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 0, + "sticky_bit": 0, + "rounding_mode": rm, + }, + { + "sign": 1, + "sig": help_values.not_max_norm_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 0, + "sticky_bit": 0, + "rounding_mode": rm, + }, + # no overflow 10 + { + "sign": 0, + "sig": help_values.not_max_norm_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 1, + "sticky_bit": 0, + "rounding_mode": rm, + }, + { + "sign": 1, + "sig": help_values.not_max_norm_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 1, + "sticky_bit": 0, + "rounding_mode": rm, + }, + # no overflow 01 + { + "sign": 0, + "sig": help_values.not_max_norm_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 0, + "sticky_bit": 1, + "rounding_mode": rm, + }, + { + "sign": 1, + "sig": help_values.not_max_norm_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 0, + "sticky_bit": 1, + "rounding_mode": rm, + }, + # no overflow 11 + { + "sign": 0, + "sig": help_values.not_max_norm_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 1, + "sticky_bit": 1, + "rounding_mode": rm, + }, + { + "sign": 1, + "sig": help_values.not_max_norm_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 1, + "sticky_bit": 1, + "rounding_mode": rm, + }, + # Round to nearest tie to even + { + "sign": 1, + "sig": help_values.not_max_norm_even_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 1, + "sticky_bit": 0, + "rounding_mode": rm, + }, + { + "sign": 0, + "sig": help_values.not_max_norm_even_sig, + "exp": help_values.not_max_norm_exp, + "round_bit": 1, + "sticky_bit": 0, + "rounding_mode": rm, + }, + ] + expected_results = [ + # carry after increment + { + "sig": (help_values.max_sig + 1) >> 1 if rm != RoundingModes.ROUND_ZERO else help_values.max_sig, + "exp": ( + help_values.not_max_norm_exp + 1 + if rm != RoundingModes.ROUND_ZERO + else help_values.not_max_norm_exp + ), + "inexact": 1, + }, + # no overflow 00 + { + "sig": help_values.not_max_norm_sig + inc_arr[0], + "exp": help_values.not_max_norm_exp, + "inexact": 0, + }, + { + "sig": help_values.not_max_norm_sig + inc_arr[4], + "exp": help_values.not_max_norm_exp, + "inexact": 0, + }, + # no overflow 01 + { + "sig": help_values.not_max_norm_sig + inc_arr[1], + "exp": help_values.not_max_norm_exp, + "inexact": 1, + }, + { + "sig": help_values.not_max_norm_sig + inc_arr[5], + "exp": help_values.not_max_norm_exp, + "inexact": 1, + }, + # no overflow 10 + { + "sig": help_values.not_max_norm_sig + inc_arr[2], + "exp": help_values.not_max_norm_exp, + "inexact": 1, + }, + { + "sig": help_values.not_max_norm_sig + inc_arr[6], + "exp": help_values.not_max_norm_exp, + "inexact": 1, + }, + # no overflow 11 + { + "sig": help_values.not_max_norm_sig + inc_arr[3], + "exp": help_values.not_max_norm_exp, + "inexact": 1, + }, + { + "sig": help_values.not_max_norm_sig + inc_arr[7], + "exp": help_values.not_max_norm_exp, + "inexact": 1, + }, + # Round to nearest tie to even + { + "sig": help_values.not_max_norm_even_sig, + "exp": help_values.not_max_norm_exp, + "inexact": 1, + }, + { + "sig": help_values.not_max_norm_even_sig, + "exp": help_values.not_max_norm_exp, + "inexact": 1, + }, + ] + + num_of_test_cases = len(test_cases) if rm == RoundingModes.ROUND_NEAREST_EVEN else len(test_cases) - 2 + + for i in range(num_of_test_cases): + + resp = yield from fpurt.rounding_request_adapter.call(test_cases[i]) + assert resp["exp"] == expected_results[i]["exp"] + assert resp["sig"] == expected_results[i]["sig"] + assert resp["inexact"] == expected_results[i]["inexact"] + + def test_process(): + yield from one_rounding_mode_test() + + with self.run_simulation(fpurt) as sim: + sim.add_process(test_process)