From 6c2fef847d262531944c2057d17c3ff0a1fdc1c4 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 7 Jul 2019 03:15:47 -0700 Subject: [PATCH] work on adding tests; test_core.py currently fails --- src/ieee754/div_rem_sqrt_rsqrt/core.py | 41 ++-- src/ieee754/div_rem_sqrt_rsqrt/test_core.py | 228 ++++++++++++++++++++ 2 files changed, 253 insertions(+), 16 deletions(-) create mode 100644 src/ieee754/div_rem_sqrt_rsqrt/test_core.py diff --git a/src/ieee754/div_rem_sqrt_rsqrt/core.py b/src/ieee754/div_rem_sqrt_rsqrt/core.py index c1b5191b..63c17217 100644 --- a/src/ieee754/div_rem_sqrt_rsqrt/core.py +++ b/src/ieee754/div_rem_sqrt_rsqrt/core.py @@ -85,15 +85,17 @@ class DivPipeCoreInputData: :attribute operation: the ``DivPipeCoreOperation`` to be computed. """ - def __init__(self, core_config): + def __init__(self, core_config, reset_less=True): """ Create a ``DivPipeCoreInputData`` instance. """ self.core_config = core_config self.dividend = Signal(core_config.bit_width + core_config.fract_width, - reset_less=True) - self.divisor_radicand = Signal(core_config.bit_width, reset_less=True) + reset_less=reset_less) + self.divisor_radicand = Signal(core_config.bit_width, + reset_less=reset_less) # FIXME: this goes into (is replaced by) self.ctx.op - self.operation = DivPipeCoreOperation.create_signal(reset_less=True) + self.operation = \ + DivPipeCoreOperation.create_signal(reset_less=reset_less) def __iter__(self): """ Get member signals. """ @@ -134,17 +136,22 @@ class DivPipeCoreInterstageData: ``core_config.fract_width * 3`` bits. """ - def __init__(self, core_config): + def __init__(self, core_config, reset_less=True): """ Create a ``DivPipeCoreInterstageData`` instance. """ self.core_config = core_config - self.divisor_radicand = Signal(core_config.bit_width, reset_less=True) + self.divisor_radicand = Signal(core_config.bit_width, + reset_less=reset_less) # FIXME: delete self.operation. already covered by self.ctx.op - self.operation = DivPipeCoreOperation.create_signal(reset_less=True) - self.quotient_root = Signal(core_config.bit_width, reset_less=True) + self.operation = \ + DivPipeCoreOperation.create_signal(reset_less=reset_less) + self.quotient_root = Signal(core_config.bit_width, + reset_less=reset_less) self.root_times_radicand = Signal(core_config.bit_width * 2, - reset_less=True) - self.compare_lhs = Signal(core_config.bit_width * 3, reset_less=True) - self.compare_rhs = Signal(core_config.bit_width * 3, reset_less=True) + reset_less=reset_less) + self.compare_lhs = Signal(core_config.bit_width * 3, + reset_less=reset_less) + self.compare_rhs = Signal(core_config.bit_width * 3, + reset_less=reset_less) def __iter__(self): """ Get member signals. """ @@ -178,11 +185,13 @@ class DivPipeCoreOutputData: fract-width of ``core_config.fract_width * 3`` bits. """ - def __init__(self, core_config): + def __init__(self, core_config, reset_less=True): """ Create a ``DivPipeCoreOutputData`` instance. """ self.core_config = core_config - self.quotient_root = Signal(core_config.bit_width, reset_less=True) - self.remainder = Signal(core_config.bit_width * 3, reset_less=True) + self.quotient_root = Signal(core_config.bit_width, + reset_less=reset_less) + self.remainder = Signal(core_config.bit_width * 3, + reset_less=reset_less) def __iter__(self): """ Get member signals. """ @@ -312,8 +321,8 @@ class DivPipeCoreCalculateStage(Elaboratable): rsqrt_rhs += self.i.root_times_radicand * (shifted_trial_bits << 1) rsqrt_rhs += self.i.divisor_radicand * shifted_trial_bits_sqrd - trial_compare_rhs = self.o.compare_rhs.like( - name=f"trial_compare_rhs_{trial_bits}") + trial_compare_rhs = Signal.like( + self.o.compare_rhs, name=f"trial_compare_rhs_{trial_bits}") with m.If(self.i.operation == DivPipeCoreOperation.UDivRem): m.d.comb += trial_compare_rhs.eq(div_rhs) diff --git a/src/ieee754/div_rem_sqrt_rsqrt/test_core.py b/src/ieee754/div_rem_sqrt_rsqrt/test_core.py new file mode 100644 index 00000000..47d168c4 --- /dev/null +++ b/src/ieee754/div_rem_sqrt_rsqrt/test_core.py @@ -0,0 +1,228 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# See Notices.txt for copyright information + +from .core import (DivPipeCoreConfig, DivPipeCoreSetupStage, + DivPipeCoreCalculateStage, DivPipeCoreFinalStage, + DivPipeCoreOperation, DivPipeCoreInputData, + DivPipeCoreInterstageData, DivPipeCoreOutputData) +from .algorithm import (FixedUDivRemSqrtRSqrt, Fixed, Operation, div_rem, + fixed_sqrt, fixed_rsqrt) +import unittest +from nmigen import Module, Elaboratable +from nmigen.hdl.ir import Fragment +from nmigen.back import rtlil +from nmigen.back.pysim import Simulator, Delay, Tick + + +def show_fixed(bits, fract_width, bit_width): + fixed = Fixed.from_bits(bits, fract_width, bit_width, False) + return f"{str(fixed)}:{repr(fixed)}" + + +def get_core_op(alg_op): + if alg_op is Operation.UDivRem: + return DivPipeCoreOperation.UDivRem + if alg_op is Operation.SqrtRem: + return DivPipeCoreOperation.SqrtRem + assert alg_op is Operation.RSqrtRem + return DivPipeCoreOperation.RSqrtRem + + +class TestCaseData: + def __init__(self, + dividend, + divisor_radicand, + alg_op, + quotient_root, + remainder, + core_config): + self.dividend = dividend + self.divisor_radicand = divisor_radicand + self.alg_op = alg_op + self.quotient_root = quotient_root + self.remainder = remainder + self.core_config = core_config + + @property + def core_op(self): + return get_core_op(self.alg_op) + + def __str__(self): + bit_width = self.core_config.bit_width + fract_width = self.core_config.fract_width + dividend_str = show_fixed(dividend, + fract_width * 2, + bit_width + fract_width) + divisor_radicand_str = show_fixed(divisor_radicand, + fract_width, + bit_width) + quotient_root_str = self.show_fixed(quotient_root, + fract_width, + bit_width) + remainder_str = self.show_fixed(remainder, + fract_width * 3, + bit_width * 3) + return f"{{dividend={dividend_str}, " \ + + f"divisor_radicand={divisor_radicand_str}, " \ + + f"op={self.alg_op.name}, " \ + + f"quotient_root={quotient_root_str}, " \ + + f"remainder={remainder_str}, " \ + + f"config={self.core_config}}}" + + +def generate_test_case(core_config, dividend, divisor_radicand, alg_op): + bit_width = core_config.bit_width + fract_width = core_config.fract_width + if alg_op is Operation.UDivRem: + if divisor_radicand == 0: + return + quotient_root, remainder = div_rem(dividend, + divisor_radicand, + bit_width * 3, + False) + remainder <<= fract_width + elif alg_op is Operation.SqrtRem: + root_remainder = fixed_sqrt(Fixed.from_bits(divisor_radicand, + fract_width, + bit_width, + False)) + quotient_root = root_remainder.root.bits + remainder = root_remainder.remainder.bits << fract_width + else: + assert alg_op is Operation.RSqrtRem + if divisor_radicand == 0: + return + root_remainder = fixed_rsqrt(Fixed.from_bits(divisor_radicand, + fract_width, + bit_width, + False)) + quotient_root = root_remainder.root.bits + remainder = root_remainder.remainder.bits + if quotient_root >= (1 << bit_width): + return + yield TestCaseData(dividend, + divisor_radicand, + alg_op, + quotient_root, + remainder, + core_config) + + +def get_test_cases(core_config, + dividend_range=None, + divisor_range=None, + radicand_range=None): + if dividend_range is None: + dividend_range = range(1 << (core_config.bit_width + + core_config.fract_width)) + if divisor_range is None: + divisor_range = range(1 << core_config.bit_width) + if radicand_range is None: + radicand_range = range(1 << core_config.bit_width) + + for alg_op in Operation: + if alg_op is Operation.UDivRem: + for dividend in dividend_range: + for divisor in divisor_range: + yield from generate_test_case(core_config, + dividend, + divisor, + alg_op) + else: + for radicand in radicand_range: + yield from generate_test_case(core_config, + dividend, + radicand, + alg_op) + + +class DivPipeCoreTestPipeline(Elaboratable): + def __init__(self, core_config): + self.setup_stage = DivPipeCoreSetupStage(core_config) + self.calculate_stages = [ + DivPipeCoreCalculateStage(core_config, stage_index) + for stage_index in range(core_config.num_calculate_stages)] + self.final_stage = DivPipeCoreFinalStage(core_config) + self.interstage_signals = [ + DivPipeCoreInterstageData(core_config, reset_less=False) + for i in range(core_config.num_calculate_stages + 1)] + self.i = DivPipeCoreInputData(core_config, reset_less=False) + self.o = DivPipeCoreOutputData(core_config, reset_less=False) + + def elaborate(self, platform): + m = Module() + stages = [self.setup_stage, *self.calculate_stages, self.final_stage] + stage_inputs = [self.i, *self.interstage_signals] + stage_outputs = [*self.interstage_signals, self.o] + for stage, input, output in zip(stages, stage_inputs, stage_outputs): + stage.setup(m, input) + m.d.sync += output.eq(stage.process(input)) + + return m + + def traces(self): + yield from self.i + for interstage_signal in self.interstage_signals: + yield from interstage_signal + yield from self.o + + +class TestDivPipeCore(unittest.TestCase): + def handle_case(self, + core_config, + dividend_range=None, + divisor_range=None, + radicand_range=None): + def gen_test_cases(): + yield from get_test_cases(core_config, + dividend_range, + divisor_range, + radicand_range) + base_name = f"div_pipe_core_bit_width_{core_config.bit_width}" + base_name += f"_fract_width_{core_config.fract_width}" + base_name += f"_radix_{1 << core_config.log2_radix}" + with self.subTest(part="synthesize"): + dut = DivPipeCoreTestPipeline(core_config) + vl = rtlil.convert(dut, ports=[*dut.i, *dut.o]) + with open(f"{base_name}.il", "w") as f: + f.write(vl) + self.fail("generated invalid rtlil") # FIXME: remove when fixed + dut = DivPipeCoreTestPipeline(core_config) + with Simulator(dut, + vcd_file=f"{base_name}.vcd", + gtkw_file=f"{base_name}.gtkw", + traces=[*dut.traces()]) as sim: + def generate_process(): + for test_case in gen_test_cases(): + yield dut.i.dividend.eq(test_case.dividend) + yield dut.i.divisor_radicand.eq(test_case.divisor_radicand) + yield dut.i.operation.eq(test_case.core_op) + yield Delay(1e-6) + yield Tick() + + def check_process(): + # sync with generator + yield + for _ in core_config.num_calculate_stages: + yield + yield + + # now synched with generator + for test_case in gen_test_cases(): + yield Delay(1e-6) + quotient_root = (yield dut.o.quotient_root) + remainder = (yield dut.o.remainder) + with self.subTest(test_case=str(test_case)): + self.assertEqual(quotient_root, + test_case.quotient_root) + self.assertEqual(remainder, test_case.remainder) + yield Tick() + sim.add_clock(2e-6) + sim.add_sync_process(generate_process) + sim.add_sync_process(check_process) + sim.run() + + def test_bit_width_8_fract_width_4_radix_2(self): + self.handle_case(DivPipeCoreConfig(bit_width=8, + fract_width=4, + log2_radix=1)) -- 2.30.2