diff --git a/artiq/gateware/dsp/tools.py b/artiq/gateware/dsp/tools.py index 5ae78bd8c..b413fae3f 100644 --- a/artiq/gateware/dsp/tools.py +++ b/artiq/gateware/dsp/tools.py @@ -29,21 +29,33 @@ def eqh(a, b): class SatAddMixin: - def sat_add(self, a): + """Signed saturating addition mixin""" + def sat_add(self, *a, limits=None, clipped=None): a = list(a) # assert all(value_bits_sign(ai)[1] for ai in a) - n = max(len(ai) for ai in a) - o = log2_int(len(a), need_pow2=False) - s = Signal((n + o, True)) - s0 = Signal((n, True)) - z = Signal((1, True)) + length = max(len(ai) for ai in a) + carry = log2_int(len(a), need_pow2=False) + full = Signal((length + carry, True)) + limited = Signal((length, True)) + clip = Signal(2) + if clipped is not None: + clipped.eq(clip) self.comb += [ - s.eq(reduce(add, a, z)), - s0[-1].eq(s[-1]), - If(s[-o-1:] == Replicate(s[-1], o + 1), - s0[:-1].eq(s[:n-1]), - ).Else( - s0[:-1].eq(Replicate(~s[-1], n - 1)), - ) + full.eq(reduce(add, a)), ] - return s0 + if limits is None: + self.comb += [ + If(full[-1-carry:] == Replicate(full[-1], carry + 1), + limited.eq(full), + clip.eq(0), + ).Else( + limited.eq(Cat(Replicate(~full[-1], length - 1), full[-1])), + clip.eq(Cat(full[-1], ~full[-1])), + ) + ] + else: + self.comb += [ + clip.eq(Cat(full < limits[0], full > limits[1])), + limited.eq(Array([full, limits[0], limits[1], 0])[clip]), + ] + return limited