From 9f168056e95b8d266a4cd2f90c0bb83b80c87088 Mon Sep 17 00:00:00 2001 From: nkrackow Date: Fri, 29 Aug 2025 08:12:17 +0200 Subject: [PATCH 1/6] replicate sign bit to fill dsp registers --- iir.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/iir.py b/iir.py index 7dfccdf..4a1bcd0 100644 --- a/iir.py +++ b/iir.py @@ -5,6 +5,7 @@ # Note: Migen translates the "out of range" pc mux selector to the last vaid mux input. from ast import Constant + from migen import * N_COEFF = 3 # [b0, b1, a0] number of coefficients for a first order iir @@ -18,8 +19,14 @@ def __init__(self): self.c = c = Signal((48, True), reset_less=True) self.mux_p = mux_p = Signal() # accumulator mux self.m = m = Signal((48, True), reset_less=True) + self.mult_out = mult_out = Signal.like(m) self.p = p = Signal((48, True), reset_less=True) - self.sync += [m.eq(a * b), p.eq(m + c), If(mux_p, p.eq(m + p))] + self.comb += mult_out.eq(a * b) + self.sync += [ + m.eq(Cat(mult_out, [mult_out[-1]] * (len(m) - len(a) - len(b)))), + p.eq(m + c), + If(mux_p, p.eq(m + p)), + ] class Iir(Module): @@ -50,7 +57,7 @@ def __init__(self, w_coeff, w_data, log2_a0, n_profiles, n_channels): ### - # Making these registers reset less results in worsend timing. + # Making these registers reset less results in worse timing. # y1 register unique for each profile y1 = Array( Array(Signal((w_data, True)) for _ in range(n_channels)) @@ -73,7 +80,7 @@ def __init__(self, w_coeff, w_data, log2_a0, n_profiles, n_channels): # 2(4) -> p2=p1+m2 # 3(5) -> retrieve data y0=clip(p2)?hold step = Signal(2) # computation step - ch_profile_last_ch = Signal(max=n_profiles + 1) # auxillary signal for muxing + ch_profile_last_ch = Signal(max=n_profiles + 1) # auxiliary signal for muxing self.submodules.dsp = dsp = Dsp() assert w_data <= len(dsp.b) assert w_coeff <= len(dsp.a) @@ -91,7 +98,14 @@ def __init__(self, w_coeff, w_data, log2_a0, n_profiles, n_channels): dsp.b.eq( x[channel_index][step] << shift_b ), # overwritten later if at step==2 - dsp.c.eq(Cat(c_rounding_offset, offset[profile_index][channel_index])), + dsp.c.eq( + Cat( + c_rounding_offset, + offset[profile_index][channel_index], + [offset[profile_index][channel_index][-1]] # extend sign bit + * (len(dsp.c) - len(dsp.a) - len(dsp.b) + (w_data - log2_a0)), + ) + ), If( stb_in & ~busy, busy.eq(1), From 446b9f7970f580a8db1171883bbfbfb2b075424b Mon Sep 17 00:00:00 2001 From: nkrackow Date: Fri, 29 Aug 2025 08:17:00 +0200 Subject: [PATCH 2/6] cleanup --- iir.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/iir.py b/iir.py index 4a1bcd0..c950956 100644 --- a/iir.py +++ b/iir.py @@ -1,11 +1,10 @@ -# First order IIR filter for multiple channels and profiles with one DSP and no blockram. +# First order IIR filter for multiple channels and profiles with one DSP and no block ram. # DSP block with MSB aligned inputs and "round half down" rounding. # # -# Note: Migen translates the "out of range" pc mux selector to the last vaid mux input. +# Note: Migen translates the "out of range" pc mux selector to the last valid mux input. from ast import Constant - from migen import * N_COEFF = 3 # [b0, b1, a0] number of coefficients for a first order iir From b4d885f5ca50a2f3ff6ac507e0959184e97b444f Mon Sep 17 00:00:00 2001 From: nkrackow Date: Fri, 29 Aug 2025 08:42:03 +0200 Subject: [PATCH 3/6] add extention comment in dsp --- iir.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/iir.py b/iir.py index c950956..ae3b08c 100644 --- a/iir.py +++ b/iir.py @@ -22,7 +22,9 @@ def __init__(self): self.p = p = Signal((48, True), reset_less=True) self.comb += mult_out.eq(a * b) self.sync += [ - m.eq(Cat(mult_out, [mult_out[-1]] * (len(m) - len(a) - len(b)))), + m.eq( + Cat(mult_out, [mult_out[-1]] * (len(m) - len(a) - len(b))) + ), # extend sign bit p.eq(m + c), If(mux_p, p.eq(m + p)), ] From 9f7831ea9a595c9ca7e058440a4bf72947d19155 Mon Sep 17 00:00:00 2001 From: nkrackow Date: Fri, 29 Aug 2025 16:45:51 +0200 Subject: [PATCH 4/6] use length-matched dsp inputs --- iir.py | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/iir.py b/iir.py index ae3b08c..d97821e 100644 --- a/iir.py +++ b/iir.py @@ -1,13 +1,14 @@ -# First order IIR filter for multiple channels and profiles with one DSP and no block ram. +# First order IIR filter for multiple channels and profiles with one DSP and no blockram. # DSP block with MSB aligned inputs and "round half down" rounding. # # # Note: Migen translates the "out of range" pc mux selector to the last valid mux input. from ast import Constant + from migen import * -N_COEFF = 3 # [b0, b1, a0] number of coefficients for a first order iir +N_COEFF = 3 # [b0, b1, a1] number of coefficients for a first order iir class Dsp(Module): @@ -15,19 +16,11 @@ def __init__(self): # xilinx dsp architecture (subset) self.a = a = Signal((25, True), reset_less=True) self.b = b = Signal((18, True), reset_less=True) - self.c = c = Signal((48, True), reset_less=True) + self.c = c = Signal((len(a) + len(b), True), reset_less=True) self.mux_p = mux_p = Signal() # accumulator mux - self.m = m = Signal((48, True), reset_less=True) - self.mult_out = mult_out = Signal.like(m) + self.m = m = Signal((len(a) + len(b), True), reset_less=True) self.p = p = Signal((48, True), reset_less=True) - self.comb += mult_out.eq(a * b) - self.sync += [ - m.eq( - Cat(mult_out, [mult_out[-1]] * (len(m) - len(a) - len(b))) - ), # extend sign bit - p.eq(m + c), - If(mux_p, p.eq(m + p)), - ] + self.sync += [m.eq(a * b), p.eq(m + c), If(mux_p, p.eq(m + p))] class Iir(Module): @@ -85,7 +78,9 @@ def __init__(self, w_coeff, w_data, log2_a0, n_profiles, n_channels): self.submodules.dsp = dsp = Dsp() assert w_data <= len(dsp.b) assert w_coeff <= len(dsp.a) - shift_c = len(dsp.a) + len(dsp.b) - w_data - (w_data - log2_a0) + shift_c = len(dsp.a) + len(dsp.b) - w_data + print(shift_c) + print(w_data) shift_a = len(dsp.a) - w_coeff shift_b = len(dsp.b) - w_data # +1 from standard sign bit @@ -99,14 +94,7 @@ def __init__(self, w_coeff, w_data, log2_a0, n_profiles, n_channels): dsp.b.eq( x[channel_index][step] << shift_b ), # overwritten later if at step==2 - dsp.c.eq( - Cat( - c_rounding_offset, - offset[profile_index][channel_index], - [offset[profile_index][channel_index][-1]] # extend sign bit - * (len(dsp.c) - len(dsp.a) - len(dsp.b) + (w_data - log2_a0)), - ) - ), + dsp.c.eq(Cat(c_rounding_offset, offset[profile_index][channel_index])), If( stb_in & ~busy, busy.eq(1), @@ -147,7 +135,7 @@ def __init__(self, w_coeff, w_data, log2_a0, n_profiles, n_channels): ch_profile_last_ch.eq(ch_profile[channel_index - 1]), [o.eq(y1[ch_profile[ch]][ch]) for ch, o in enumerate(outp)], # clipping to positive output range - y0_clipped.eq(dsp.p >> shift_c), + y0_clipped.eq(dsp.p >> (shift_c - (w_data - log2_a0))), If( dsp.p[-n_sign:] != 0, # if out of output range y0_clipped.eq((1 << w_data - 1) - 1), From 4a0d325aca57f0ccf1592c71919cfcd49a49f22c Mon Sep 17 00:00:00 2001 From: nkrackow Date: Fri, 29 Aug 2025 16:58:09 +0200 Subject: [PATCH 5/6] rm prints --- iir.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/iir.py b/iir.py index d97821e..17dc49f 100644 --- a/iir.py +++ b/iir.py @@ -79,8 +79,6 @@ def __init__(self, w_coeff, w_data, log2_a0, n_profiles, n_channels): assert w_data <= len(dsp.b) assert w_coeff <= len(dsp.a) shift_c = len(dsp.a) + len(dsp.b) - w_data - print(shift_c) - print(w_data) shift_a = len(dsp.a) - w_coeff shift_b = len(dsp.b) - w_data # +1 from standard sign bit From 1faf6f3514878d0788e43bd3b30732b51b9b5484 Mon Sep 17 00:00:00 2001 From: nkrackow Date: Mon, 15 Sep 2025 16:16:10 +0200 Subject: [PATCH 6/6] simply hardcode correct sizes --- iir.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/iir.py b/iir.py index 17dc49f..6e97263 100644 --- a/iir.py +++ b/iir.py @@ -5,7 +5,6 @@ # Note: Migen translates the "out of range" pc mux selector to the last valid mux input. from ast import Constant - from migen import * N_COEFF = 3 # [b0, b1, a1] number of coefficients for a first order iir @@ -16,9 +15,9 @@ def __init__(self): # xilinx dsp architecture (subset) self.a = a = Signal((25, True), reset_less=True) self.b = b = Signal((18, True), reset_less=True) - self.c = c = Signal((len(a) + len(b), True), reset_less=True) + self.c = c = Signal((41, True), reset_less=True) self.mux_p = mux_p = Signal() # accumulator mux - self.m = m = Signal((len(a) + len(b), True), reset_less=True) + self.m = m = Signal((43, True), reset_less=True) self.p = p = Signal((48, True), reset_less=True) self.sync += [m.eq(a * b), p.eq(m + c), If(mux_p, p.eq(m + p))] @@ -78,7 +77,7 @@ def __init__(self, w_coeff, w_data, log2_a0, n_profiles, n_channels): self.submodules.dsp = dsp = Dsp() assert w_data <= len(dsp.b) assert w_coeff <= len(dsp.a) - shift_c = len(dsp.a) + len(dsp.b) - w_data + shift_c = len(dsp.a) + len(dsp.b) - w_data - (w_data - log2_a0) shift_a = len(dsp.a) - w_coeff shift_b = len(dsp.b) - w_data # +1 from standard sign bit @@ -133,7 +132,7 @@ def __init__(self, w_coeff, w_data, log2_a0, n_profiles, n_channels): ch_profile_last_ch.eq(ch_profile[channel_index - 1]), [o.eq(y1[ch_profile[ch]][ch]) for ch, o in enumerate(outp)], # clipping to positive output range - y0_clipped.eq(dsp.p >> (shift_c - (w_data - log2_a0))), + y0_clipped.eq(dsp.p >> shift_c), If( dsp.p[-n_sign:] != 0, # if out of output range y0_clipped.eq((1 << w_data - 1) - 1),