/ test / func_blocks / fu / fpu / test_close_path.py
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)