work around yosys bug with Switch() over high-bit-width Value instances
[nmigen-gf.git] / src / nmigen_gf / hdl / test / test_cldivrem.py
1 # SPDX-License-Identifier: LGPL-3-or-later
2 # Copyright 2022 Jacob Lifshay
3
4 # Funded by NLnet Assure Programme 2021-02-052, https://nlnet.nl/assure part
5 # of Horizon 2020 EU Programme 957073.
6
7 import unittest
8 from nmigen.hdl.ast import (AnyConst, Assert, Signal, Const, unsigned, Cat)
9 from nmigen.hdl.dsl import Module
10 from nmutil.formaltest import FHDLTestCase
11 from nmigen_gf.hdl.cldivrem import (equal_leading_zero_count_reference,
12 EqualLeadingZeroCount)
13 from nmigen.sim import Delay
14 from nmutil.sim_util import do_sim, hash_256
15
16
17 class TestEqualLeadingZeroCount(FHDLTestCase):
18 def tst(self, width, full):
19 dut = EqualLeadingZeroCount(width)
20 self.assertEqual(dut.a.shape(), unsigned(width))
21 self.assertEqual(dut.b.shape(), unsigned(width))
22 self.assertEqual(dut.out.shape(), unsigned(1))
23
24 def case(a, b):
25 assert isinstance(a, int)
26 assert isinstance(b, int)
27 expected = a.bit_length() == b.bit_length()
28 with self.subTest(a=hex(a), b=hex(b),
29 expected=expected):
30 reference = equal_leading_zero_count_reference(a, b, width)
31 with self.subTest(reference=reference):
32 self.assertEqual(expected, reference)
33
34 with self.subTest(a=hex(a), b=hex(b),
35 expected=expected):
36 yield dut.a.eq(a)
37 yield dut.b.eq(b)
38 yield Delay(1e-6)
39 out = yield dut.out
40 with self.subTest(out=out):
41 self.assertEqual(expected, out)
42
43 def process():
44 if full:
45 for a in range(1 << width):
46 for b in range(1 << width):
47 yield from case(a, b)
48 else:
49 for i in range(100):
50 a = hash_256(f"eqlzc input a {i}")
51 a = Const.normalize(a, dut.a.shape())
52 b = hash_256(f"eqlzc input b {i}")
53 b = Const.normalize(b, dut.b.shape())
54 yield from case(a, b)
55
56 with do_sim(self, dut, [dut.a, dut.b, dut.out]) as sim:
57 sim.add_process(process)
58 sim.run()
59
60 def tst_formal(self, width):
61 dut = EqualLeadingZeroCount(width)
62 m = Module()
63 m.submodules.dut = dut
64 m.d.comb += dut.a.eq(AnyConst(width))
65 m.d.comb += dut.b.eq(AnyConst(width))
66 # use a bunch of Value.matches() and boolean logic rather than a
67 # giant Switch()/If() to avoid
68 # https://github.com/YosysHQ/yosys/issues/3268
69 expected_v = False
70 for leading_zeros in range(width + 1):
71 pattern = '0' * leading_zeros + '1' + '-' * width
72 pattern = pattern[0:width]
73 a_has_count = Signal(name=f"a_has_{leading_zeros}")
74 b_has_count = Signal(name=f"b_has_{leading_zeros}")
75 m.d.comb += [
76 a_has_count.eq(dut.a.matches(pattern)),
77 b_has_count.eq(dut.b.matches(pattern)),
78 ]
79 expected_v |= a_has_count & b_has_count
80 expected = Signal()
81 m.d.comb += expected.eq(expected_v)
82 m.d.comb += Assert(dut.out == expected)
83 self.assertFormal(m)
84
85 def test_64(self):
86 self.tst(64, full=False)
87
88 def test_8(self):
89 self.tst(8, full=False)
90
91 def test_3(self):
92 self.tst(3, full=True)
93
94 def test_formal_64(self):
95 self.tst_formal(64)
96
97 def test_formal_8(self):
98 self.tst_formal(8)
99
100 def test_formal_3(self):
101 self.tst_formal(3)
102
103 # TODO: add TestCLDivRem
104
105
106 if __name__ == "__main__":
107 unittest.main()