b684c65f2a8245092d13ae433fa49db849bc62da
1 # SPDX-License-Identifier: LGPL-3-or-later
3 Bitwise logic operators implemented using a look-up table, like LUTs in
4 FPGAs. Inspired by x86's `vpternlog[dq]` instructions.
6 https://bugs.libre-soc.org/show_bug.cgi?id=745
7 https://www.felixcloutier.com/x86/vpternlogd:vpternlogq
10 from nmigen
.hdl
.ast
import Array
, Cat
, Repl
, Signal
11 from nmigen
.hdl
.dsl
import Module
12 from nmigen
.hdl
.ir
import Elaboratable
15 class BitwiseMux(Elaboratable
):
16 """ Mux, but treating input/output Signals as bit vectors, rather than
17 integers. This means each bit in the output is independently multiplexed
18 based on the corresponding bit in each of the inputs.
21 def __init__(self
, width
):
22 self
.sel
= Signal(width
)
23 self
.t
= Signal(width
)
24 self
.f
= Signal(width
)
25 self
.output
= Signal(width
)
27 def elaborate(self
, platform
):
29 m
.d
.comb
+= self
.output
.eq((~self
.sel
& self
.f
) |
(self
.sel
& self
.t
))
33 class BitwiseLut(Elaboratable
):
34 """ Bitwise logic operators implemented using a look-up table, like LUTs in
35 FPGAs. Inspired by x86's `vpternlog[dq]` instructions.
37 Each output bit `i` is set to `lut[Cat(inp[i] for inp in self.inputs)]`
40 def __init__(self
, input_count
, width
):
43 the number of inputs. ternlog-style instructions have 3 inputs.
45 the number of bits in each input/output.
47 self
.input_count
= input_count
51 return Signal(width
, name
=f
"input{i}")
52 self
.inputs
= tuple(inp(i
) for i
in range(input_count
))
54 self
.output
= Signal(width
)
56 self
.lut
= Signal(2 ** input_count
)
57 """ the look-up table. Is `2 ** input_count` bits wide."""
59 def elaborate(self
, platform
):
61 lut_array
= Array(self
.lut
)
62 for bit
in range(self
.width
):
63 index
= Cat(inp
[bit
] for inp
in self
.inputs
)
64 m
.d
.comb
+= self
.output
[bit
].eq(lut_array
[index
])
68 class TreeBitwiseLut(Elaboratable
):
69 """ Tree-based version of BitwiseLut. See BitwiseLut for API documentation.
72 def __init__(self
, input_count
, width
):
73 self
.input_count
= input_count
77 return Signal(width
, name
=f
"input{i}")
78 self
.inputs
= tuple(inp(i
) for i
in range(input_count
))
79 self
.output
= Signal(width
)
80 self
.lut
= Signal(2 ** input_count
)
82 self
._build
_mux
_inputs
()
84 def _make_key_str(self
, *sel_values
):
85 k
= ['x'] * self
.input_count
86 for i
, v
in enumerate(sel_values
):
87 k
[i
] = '1' if v
else '0'
88 return '0b' + ''.join(reversed(k
))
90 def _build_mux_inputs(self
, *sel_values
):
91 name
= f
"mux_input_{self._make_key_str(*sel_values)}"
92 self
._mux
_inputs
[sel_values
] = Signal(self
.width
, name
=name
)
93 if len(sel_values
) < self
.input_count
:
94 self
._build
_mux
_inputs
(*sel_values
, False)
95 self
._build
_mux
_inputs
(*sel_values
, True)
97 def elaborate(self
, platform
):
99 m
.d
.comb
+= self
.output
.eq(self
._mux
_inputs
[()])
100 for sel_values
, v
in self
._mux
_inputs
.items():
101 if len(sel_values
) < self
.input_count
:
102 mux_name
= f
"mux_{self._make_key_str(*sel_values)}"
103 mux
= BitwiseMux(self
.width
)
104 setattr(m
.submodules
, mux_name
, mux
)
106 mux
.f
.eq(self
._mux
_inputs
[(*sel_values
, False)]),
107 mux
.t
.eq(self
._mux
_inputs
[(*sel_values
, True)]),
108 mux
.sel
.eq(self
.inputs
[len(sel_values
)]),
113 for i
in range(self
.input_count
):
116 m
.d
.comb
+= v
.eq(Repl(self
.lut
[lut_index
], self
.width
))