1 # SPDX-License-Identifier: LGPL-3-or-later
2 # Copyright 2022 Jacob Lifshay programmerjake@gmail.com
4 # Funded by NLnet Assure Programme 2021-02-052, https://nlnet.nl/assure part
5 # of Horizon 2020 EU Programme 957073.
7 """ Carry-less Division and Remainder.
9 https://bugs.libre-soc.org/show_bug.cgi?id=784
12 from nmigen
.hdl
.ir
import Elaboratable
13 from nmigen
.hdl
.ast
import Signal
, Cat
14 from nmigen
.hdl
.dsl
import Module
17 def equal_leading_zero_count_reference(a
, b
, width
):
18 """checks if `clz(a) == clz(b)`.
19 Reference code for algorithm used in `EqualLeadingZeroCount`.
21 assert isinstance(width
, int) and 0 <= width
22 assert isinstance(a
, int) and 0 <= a
< (1 << width
)
23 assert isinstance(b
, int) and 0 <= b
< (1 << width
)
24 eq
= True # both have no leading zeros so far...
25 for i
in range(width
):
28 eq
= True # both have no leading zeros so far...
30 eq
= False # different number of leading zeros
33 eq
= False # different number of leading zeros
35 pass # propagate results from lower bits
39 class EqualLeadingZeroCount(Elaboratable
):
40 """checks if `clz(a) == clz(b)`.
44 the width in bits of `a` and `b`.
45 a: Signal of width `width`
47 b: Signal of width `width`
49 out: Signal of width `1`
50 output, set if the number of leading zeros in `a` is the same as in
54 def __init__(self
, width
):
55 assert isinstance(width
, int)
57 self
.a
= Signal(width
)
58 self
.b
= Signal(width
)
61 def elaborate(self
, platform
):
62 # the operation is converted into calculation of the carry-out of a
63 # binary addition, allowing FPGAs to re-use their specialized
64 # carry-propagation logic. This should be simplified by yosys to
65 # remove the extraneous xor gates from addition when targeting
66 # FPGAs/ASICs, so no efficiency is lost.
68 # see `equal_leading_zero_count_reference` for a Python version of
69 # the algorithm, but without conversion to carry-propagation.
71 addend1
= Signal(self
.width
)
72 addend2
= Signal(self
.width
)
73 for i
in range(self
.width
):
74 with m
.Switch(Cat(self
.a
[i
], self
.b
[i
])):
76 # both have no leading zeros so far, so set carry
81 with m
.Case('01', '10'):
82 # different number of leading zeros, so clear carry
88 # propagate results from lower bits
93 sum = Signal(self
.width
+ 1)
94 carry_in
= 1 # both have no leading zeros so far, so set carry
95 m
.d
.comb
+= sum.eq(addend1
+ addend2
+ carry_in
)
96 m
.d
.comb
+= self
.out
.eq(sum[self
.width
]) # out is carry-out