Nie pracowałem jeszcze z filtrami IIR, ale jeśli potrzebujesz tylko obliczyć podane równanie
y[n] = y[n-1]*b1 + x[n]
raz na cykl CPU możesz użyć potokowania.
W jednym cyklu wykonujesz mnożenie, aw jednym cyklu musisz wykonać sumowanie dla każdej próbki wejściowej. Oznacza to, że Twój układ FPGA musi być w stanie wykonać pomnożenie w jednym cyklu, jeśli jest taktowany przy danej częstotliwości próbkowania! Następnie wystarczy wykonać pomnożenie bieżącej próbki ORAZ sumowanie wyniku pomnożenia ostatniej próbki równolegle. Spowoduje to stałe opóźnienie przetwarzania o 2 cykle.
Ok, rzućmy okiem na formułę i zaprojektuj potok:
y[n] = y[n-1]*b1 + x[n]
Twój kod potoku może wyglądać następująco:
output <= last_output_times_b1 + last_input
last_output_times_b1 <= output * b1;
last_input <= input
Zauważ, że wszystkie trzy polecenia muszą być wykonywane równolegle, a zatem „wyjście” w drugim wierszu wykorzystuje dane wyjściowe z ostatniego cyklu zegara!
Nie pracowałem dużo z Verilog, więc składnia tego kodu jest najprawdopodobniej niepoprawna (np. Brak szerokości bitów sygnałów wejściowych / wyjściowych; składnia wykonania dla mnożenia). Powinieneś jednak pomyśleć:
module IIRFilter( clk, reset, x, b, y );
input clk, reset, x, b;
output y;
reg y, t, t2;
wire clk, reset, x, b;
always @ (posedge clk or posedge reset)
if (reset) begin
y <= 0;
t <= 0;
t2 <= 0;
end else begin
y <= t + t2;
t <= mult(y, b);
t2 <= x
end
endmodule
PS: Być może jakiś doświadczony programista Verilog mógłby edytować ten kod i później usunąć ten komentarz i komentarz nad kodem. Dzięki!
PPS: W przypadku, gdy twój współczynnik „b1” jest stałą, możesz być w stanie zoptymalizować projekt poprzez wdrożenie specjalnego mnożnika, który przyjmuje tylko jedno wejście skalarne i oblicza tylko „czasy b1”.
Odpowiedź na: „Niestety, w rzeczywistości jest to równoważne y [n] = y [n-2] * b1 + x [n]. Wynika to z dodatkowego etapu potoku.” jako komentarz do starej wersji odpowiedzi
Tak, tak naprawdę było to właściwe dla następującej starej (NIEPRAWIDŁOWEJ) wersji:
always @ (posedge clk or posedge reset)
if (reset) begin
t <= 0;
end else begin
y <= t + x;
t <= mult(y, b);
end
Mam nadzieję, że poprawiłem ten błąd, opóźniając wartości wejściowe również w drugim rejestrze:
always @ (posedge clk or posedge reset)
if (reset) begin
y <= 0;
t <= 0;
t2 <= 0;
end else begin
y <= t + t2;
t <= mult(y, b);
t2 <= x
end
Aby upewnić się, że tym razem działa poprawnie, zobaczmy, co dzieje się w pierwszych kilku cyklach. Zauważ, że pierwsze 2 cykle generują mniej lub więcej (zdefiniowanych) śmieci, ponieważ żadne poprzednie wartości wyjściowe (np. Y [-1] == ??) nie są dostępne. Rejestr y jest inicjalizowany wartością 0, co jest równoznaczne z przyjęciem y [-1] == 0.
Pierwszy cykl (n = 0):
BEFORE: INPUT (x=x[0], b); REGISTERS (t=0, t2=0, y=0)
y <= t + t2; == 0
t <= mult(y, b); == y[-1] * b = 0
t2 <= x == x[0]
AFTERWARDS: REGISTERS (t=0, t2=x[0], y=0), OUTPUT: y[0]=0
Drugi cykl (n = 1):
BEFORE: INPUT (x=x[1], b); REGISTERS (t=0, t2=x[0], y=y[0])
y <= t + t2; == 0 + x[0]
t <= mult(y, b); == y[0] * b
t2 <= x == x[1]
AFTERWARDS: REGISTERS (t=y[0]*b, t2=x[1], y=x[0]), OUTPUT: y[1]=x[0]
Trzeci cykl (n = 2):
BEFORE: INPUT (x=x[2], b); REGISTERS (t=y[0]*b, t2=x[1], y=y[1])
y <= t + t2; == y[0]*b + x[1]
t <= mult(y, b); == y[1] * b
t2 <= x == x[2]
AFTERWARDS: REGISTERS (t=y[1]*b, t2=x[2], y=y[0]*b+x[1]), OUTPUT: y[2]=y[0]*b+x[1]
Czwarty cykl (n = 3):
BEFORE: INPUT (x=x[3], b); REGISTERS (t=y[1]*b, t2=x[2], y=y[2])
y <= t + t2; == y[1]*b + x[2]
t <= mult(y, b); == y[2] * b
t2 <= x == x[3]
AFTERWARDS: REGISTERS (t=y[2]*b, t2=x[3], y=y[1]*b+x[2]), OUTPUT: y[3]=y[1]*b+x[2]
Widzimy, że zaczynając od cylce n = 2 otrzymujemy następujące dane wyjściowe:
y[2]=y[0]*b+x[1]
y[3]=y[1]*b+x[2]
co jest równoważne z
y[n]=y[n-2]*b + x[n-1]
y[n]=y[n-1-l]*b1 + x[n-l], where l = 1
y[n+l]=y[n-1]*b1 + x[n], where l = 1
Jak wspomniano powyżej, wprowadzamy dodatkowe opóźnienie wynoszące 1 = 1 cykli. Oznacza to, że twoje wyjście y [n] jest opóźnione o opóźnienie l = 1. Oznacza to, że dane wyjściowe są równoważne, ale są opóźnione o jeden „indeks”. Aby być bardziej zrozumiałym: dane wyjściowe są opóźnione o 2 cykle, ponieważ potrzebny jest jeden (normalny) cykl zegara i dodawany jest 1 dodatkowy (opóźnienie 1 = 1) cykl zegara dla etapu pośredniego.
Oto szkic, aby graficznie zobrazować przepływ danych:
PS: Dziękuję za dokładne przyjrzenie się mojemu kodowi. Więc też się czegoś nauczyłem! ;-) Daj mi znać, czy ta wersja jest poprawna lub jeśli pojawią się kolejne problemy.