test_close_path.py
1 from coreblocks.func_blocks.fu.fpu.close_path import * 2 from coreblocks.func_blocks.fu.fpu.fpu_common import FPUParams 3 from transactron import TModule 4 from transactron.lib import AdapterTrans 5 from transactron.testing import * 6 from amaranth import * 7 8 9 class TestClosePath(TestCaseWithSimulator): 10 class ClosePathModuleTest(Elaboratable): 11 def __init__(self, params: FPUParams): 12 self.params = params 13 14 def elaborate(self, platform): 15 m = TModule() 16 m.submodules.close = close_path = self.close_path_module = ClosePathModule(fpu_params=self.params) 17 m.submodules.request = self.close_path_request_adapter = TestbenchIO( 18 AdapterTrans.create(close_path.close_path_request) 19 ) 20 return m 21 22 def test_manual(self): 23 params = FPUParams(sig_width=24, exp_width=8) 24 close_path = TestClosePath.ClosePathModuleTest(params) 25 26 async def normal_cases(sim: TestbenchContext): 27 test_cases = [ 28 { # case 1 zero adder no shift 29 "sig_a": 0b111110000000000000000000, 30 "sig_b": 0b100011000000000000000000, 31 "guard_bit": 1, 32 }, 33 { # case 2 one adder no shift 34 "sig_a": 0b111110000000000000000001, 35 "sig_b": 0b100011111100000000000000, 36 "guard_bit": 1, 37 }, 38 { # case 3 zero adder shift 39 "sig_a": 0b110010000000000000000000, 40 "sig_b": 0b010010000000000000000000, 41 "guard_bit": 1, 42 }, 43 { # case 4 one adder shift 44 "sig_a": 0b110010000000000000000000, 45 "sig_b": 0b010010000000000000000000, 46 "guard_bit": 0, 47 }, 48 { # case 5 correction shift needed 49 "sig_a": 0b100000000000000000000000, 50 "sig_b": 0b100000100000000000000000, 51 "guard_bit": 1, 52 }, 53 ] 54 expected_results = [ 55 { 56 "out_exp": 100, 57 "out_sig": 0b100001000000000000000000, 58 "output_round": 1, 59 }, 60 {"out_exp": 100, "out_sig": 0b100001111100000000000010, "output_round": 1}, # case 2 61 {"out_exp": 97, "out_sig": 0b100000000000000000000100, "output_round": 0}, # case 3 62 {"out_exp": 97, "out_sig": 0b100000000000000000001000, "output_round": 0}, # case 4 63 {"out_exp": 94, "out_sig": 0b100000000000000000100000, "output_round": 0}, # case 5 64 ] 65 66 for rm in RoundingModes: 67 for i in range(5): 68 if rm == RoundingModes.ROUND_NEAREST_AWAY: 69 if i == 0: 70 continue 71 input_dict = { 72 "r_sign": 1 if rm == RoundingModes.ROUND_DOWN else 0, 73 "sig_a": test_cases[i]["sig_a"], 74 "sig_b": test_cases[i]["sig_b"], 75 "exp": 100, 76 "rounding_mode": rm, 77 "guard_bit": test_cases[i]["guard_bit"], 78 } 79 if rm == RoundingModes.ROUND_UP and i == 0: 80 input_dict["r_sign"] = 1 81 if rm == RoundingModes.ROUND_DOWN and i == 0: 82 input_dict["r_sign"] = 0 83 if rm == RoundingModes.ROUND_ZERO and i == 1: 84 input_dict["guard_bit"] = 0 85 86 resp = await close_path.close_path_request_adapter.call(sim, input_dict) 87 assert resp["out_sig"] == expected_results[i]["out_sig"] 88 assert resp["out_exp"] == expected_results[i]["out_exp"] 89 if rm == RoundingModes.ROUND_ZERO and i == 1: 90 assert resp["output_round"] == 0 91 else: 92 assert resp["output_round"] == expected_results[i]["output_round"] 93 94 async def corner_cases(sim: TestbenchContext): 95 test_cases = [ 96 { # case 1 result becomes subnormal 97 "r_sign": 0, 98 "sig_a": 0b100100000000000000000000, 99 "sig_b": 0b100011000000000000000000, 100 "exp": 1, 101 "rounding_mode": RoundingModes.ROUND_NEAREST_EVEN, 102 "guard_bit": 0, 103 }, 104 { # case 2 subtract two subnormals 105 "r_sign": 0, 106 "sig_a": 0b000001100000000000000000, 107 "sig_b": 0b111111000000000000000000, 108 "exp": 0, 109 "rounding_mode": RoundingModes.ROUND_NEAREST_EVEN, 110 "guard_bit": 1, 111 }, 112 { # case 3 subtract subnormal from normal 113 "r_sign": 0, 114 "sig_a": 0b110000000000000000000000, 115 "sig_b": 0b111110000010000000000000, 116 "exp": 100, 117 "rounding_mode": RoundingModes.ROUND_NEAREST_EVEN, 118 "guard_bit": 0, 119 }, 120 { # case 4 shift correction turns number into subnormal 121 "r_sign": 0, 122 "sig_a": 0b100000000000000000000000, 123 "sig_b": 0b100000100000000000000000, 124 "exp": 6, 125 "rounding_mode": RoundingModes.ROUND_NEAREST_EVEN, 126 "guard_bit": 1, 127 }, 128 { # case 5 large shift 129 "r_sign": 0, 130 "sig_a": 0b100000000000000000000000, 131 "sig_b": 0b100000000000000000000000, 132 "exp": 100, 133 "rounding_mode": RoundingModes.ROUND_NEAREST_EVEN, 134 "guard_bit": 1, 135 }, 136 { # case 6 subtract zero 137 "r_sign": 0, 138 "sig_a": 0b100000100000000000000000, 139 "sig_b": 0b111111111111111111111111, 140 "exp": 100, 141 "rounding_mode": RoundingModes.ROUND_NEAREST_EVEN, 142 "guard_bit": 0, 143 }, 144 { # case 7 result is zero 145 "r_sign": 0, 146 "sig_a": 0b100000000011000000000000, 147 "sig_b": 0b011111111100111111111111, 148 "exp": 100, 149 "rounding_mode": RoundingModes.ROUND_NEAREST_EVEN, 150 "guard_bit": 0, 151 }, 152 ] 153 expected_results = [ 154 { 155 "out_exp": 0, 156 "out_sig": 0b000111000000000000000001, 157 "output_round": 0, 158 }, 159 {"out_exp": 0, "out_sig": 0b000000100000000000000000, "output_round": 1}, # case 2 160 {"out_exp": 100, "out_sig": 0b101110000010000000000001, "output_round": 0}, # case 3 161 {"out_exp": 0, "out_sig": 0b010000000000000000010000, "output_round": 0}, # case 4 162 {"out_exp": 76, "out_sig": 0b100000000000000000000000, "output_round": 0}, # case 5 163 {"out_exp": 100, "out_sig": 0b100000100000000000000000, "output_round": 0}, # case 6 164 {"out_exp": 0, "out_sig": 0b000000000000000000000000, "output_round": 0}, # case 7 165 ] 166 for i in range(len(test_cases)): 167 resp = await close_path.close_path_request_adapter.call(sim, test_cases[i]) 168 assert resp["out_sig"] == expected_results[i]["out_sig"] 169 assert resp["out_exp"] == expected_results[i]["out_exp"] 170 assert resp["output_round"] == expected_results[i]["output_round"] 171 172 async def test_process(sim: TestbenchContext): 173 await normal_cases(sim) 174 await corner_cases(sim) 175 176 with self.run_simulation(close_path) as sim: 177 sim.add_testbench(test_process)