diff --git a/litex/gen/common.py b/litex/gen/common.py index f4ab41029..c1cfebaf3 100644 --- a/litex/gen/common.py +++ b/litex/gen/common.py @@ -23,6 +23,31 @@ def reverse_bytes(s): return Cat(*[s[i*8:min((i + 1)*8, len(s))] for i in reversed(range(n))]) +# Reduction ---------------------------------------------------------------------------------------- + +from functools import reduce +from operator import and_, or_, not_, xor + +def Reduce(operator, value): + # List of supported Operators. + operators = { + "AND" : and_, + "OR" : or_, + "NOR" : not_, + "XOR" : xor, + } + + # Switch to upper-case. + operator = operator.upper() + + # Check if provided operator is supported. + if operator not in operators.keys(): + supported = ", ".join(operators.keys()) + raise ValueError(f"Reduce does not support {operator} operator; supported: {supported}.") + + # Return Python's reduction. + return reduce(operators[operator], value) + # LiteX Module ------------------------------------------------------------------------------------- class LiteXModule(Module, AutoCSR, AutoDoc): diff --git a/test/test_reduce.py b/test/test_reduce.py new file mode 100644 index 000000000..ebecfcdad --- /dev/null +++ b/test/test_reduce.py @@ -0,0 +1,52 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2022 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +import unittest +import random + +from migen import * + +from litex.gen import * + +class TestReduce(unittest.TestCase): + def reduce_test(self, operator, value, reduced): + class DUT(Module): + def __init__(self): + self.errors = 0 + self.reduced = Reduce(operator, value) + + def checker(self): + yield + if (yield self.reduced) != reduced: + self.errors += 1 + + dut = DUT() + run_simulation(dut, [dut.checker()]) + self.assertEqual(dut.errors, 0) + + def test_reduced_and(self): + self.reduce_test(operator="AND", value=Constant(0b00, 2), reduced=0b0) + self.reduce_test(operator="AND", value=Constant(0b01, 2), reduced=0b0) + self.reduce_test(operator="AND", value=Constant(0b10, 2), reduced=0b0) + self.reduce_test(operator="AND", value=Constant(0b11, 2), reduced=0b1) + + def test_reduced_or(self): + self.reduce_test(operator="OR", value=Constant(0b00, 2), reduced=0b0) + self.reduce_test(operator="OR", value=Constant(0b01, 2), reduced=0b1) + self.reduce_test(operator="OR", value=Constant(0b10, 2), reduced=0b1) + self.reduce_test(operator="OR", value=Constant(0b11, 2), reduced=0b1) + + def test_reduced_nor(self): + self.reduce_test(operator="NOR", value=Constant(0b00, 2), reduced=0b1) + self.reduce_test(operator="NOR", value=Constant(0b01, 2), reduced=0b0) + self.reduce_test(operator="NOR", value=Constant(0b10, 2), reduced=0b0) + self.reduce_test(operator="NOR", value=Constant(0b11, 2), reduced=0b0) + + def test_reduced_nor(self): + self.reduce_test(operator="XOR", value=Constant(0b00, 2), reduced=0b0) + self.reduce_test(operator="XOR", value=Constant(0b01, 2), reduced=0b1) + self.reduce_test(operator="XOR", value=Constant(0b10, 2), reduced=0b1) + self.reduce_test(operator="XOR", value=Constant(0b11, 2), reduced=0b0)