Hi guys!
I've written another IIR implementation after Directform 1 to implement
a high pass filter in my current design, the IIR will be combined with a
CIC that downsamples the sampling rate from 10MHz to ~79KHz. My filter
coefficients for the IIR are as follows:
> A1 = -1,93178> A2 = 0,93403> B0 = 0,96645> B1 = -1,93291> B2 = 0,96645
The coefficients are scaled by 2^30 and then divided by this value after
the multiplication. My implementation works with 4 guard bits, as you
can see Idle-State for nX0.
Although my simulation looks OK (to me), it doesn't work in the real
FPGA so frequencies below 1KHz are still in the signal. How do I
properly test my IIR and is there maybe something I'm missing in my
implementation?
Bernhard K. wrote:> maybe I don't see it but somehow I miss a statement like:> "if rising_edge(iclk) then" or " wait until rising_edge(iclk);"> in your VHDL...
Holy crap you're right!
Some code-blindness >_>
I'll compile again and report if this was my (really) stupid mistake :D
So I've implemented the clocked version :D
I did a test on the filter via an 1V amplitude and a frequency sweep
from 0 to 3000Hz. It seems to filter a bit, but not nearly as good as
I've simulated.
It looks that I'm missing something, the curve looks like I'm on track
but is my division flawed at some point?
Hi,
1. You should sum up the multiplication results and not the truncated
multiplication results. Using the truncated values will increase
rounding noise.
2. probably this line will lead to a timing violation:
nYOUT <=
std_logic_vector(signed(nDX0)+signed(nDX1)+signed(nDX2)-signed(nDY1)-sig
ned(nDY2));
Tom
> 1. You should sum up the multiplication results and not the truncated> multiplication results. Using the truncated values will increase> rounding noise.
Done, values are a bit different and simulation looks ok, the curve
looks identical. Will try this in my FPGA, new code and testbench are
added if someone wants a closer look.
> 2. probably this line will lead to a timing violation:> nYOUT <=> std_logic_vector(signed(nDX0)+signed(nDX1)+signed(nDX2)-signed(nDY1)-sig
ned(nDY2));
According to Quartus it seems OK though, no timing problems mentioned by
TimeQuest.
So far I've made two versions of the IIR, one is more async
(IIR_Biquad_II.vhd) and one uses a state machine (IIRDF1.vhd). The more
async one was derived from a project on the internet and it seems to
work as a high pass (but gives a weird curve as low pass). I've
implemented both in the FPGA and the async gives a really nice curve,
while the one with the state machine has some weird effects. Can someone
tell me why this is? I really have no clue what should cause this :-/
By the way, my own state machine implementation works fairly well as a
low pass, as you can see on the picture with the two additional signals
(2nd and 4th order LP).
"it seems to work as a high pass (but gives a weird curve as low pass)"
As I did before I would recommend that you implement a testbench to
check every bit of the computation and compare it for example with an
implementation in lua, java or what so ever. If everything is checked in
that way there will be no more "It seems". It's a little bit of work for
the first filter, but you will easily check other filters afterwards.
Martin O. wrote:> "it seems to work as a high pass (but gives a weird curve as low pass)">> As I did before I would recommend that you implement a testbench to> check every bit of the computation and compare it for example with an> implementation in lua, java or what so ever.
That's what I did, the values look quite nice though and it behaves like
a filter should do (from my understanding).
So I did a new VHDL Testbench and my simulation shows that both
implementations are not as equal as I've thought with the jumps as test
values. I'm using a DDS with 100KHz and full amplitude as Input. As you
can see in the picture, the DF1 has kind of a sign in it, which should
explain the behaviour of the filter. Now I need to find out why this is
:-/
It's probably not sufficient to qualitatively compare output values.
I know it's a pain, but sometimes it's the only cure: Compare every bit
of every arithmetic operation and variable for some (initial) steps.
Did something like that before, this is still directform 1 with just
some async calculations and the filter curves look the same as with my
state machine implementation. Maybe I'll try your implementation of the
DF2, but as far as I can see there is no overflow in the registers, even
with 8 guard bits there seems to be no issue. Can you provide a proper
testbench for your filter?
1
LIBRARYieee;
2
USEieee.std_logic_1164.ALL;
3
useieee.NUMERIC_STD.ALL;
4
useieee.std_logic_signed.all;
5
6
7
entityIIRDF1is
8
generic(
9
INPUT_WIDTH:integer:=64;
10
QFORMAT:integer:=30;
11
B0:integer:=409494;
12
B1:integer:=818988;
13
B2:integer:=409494;
14
A1:integer:=-3954428;
15
A2:integer:=1398100
16
);
17
18
port(
19
iCLK:instd_logic;
20
iRESET_N:instd_logic;
21
inewValue:instd_logic;-- indicates a new input value
22
iIIR_RX:instd_logic_vector(INPUT_WIDTH-1downto0);-- singed is expected
So sieht meine Testbench aus. Die Koeffizienten sind besonders einfach
und ich teste damit die grundlegende Arithmetik. Ob Überläufe
stattfinden teste ich immer am echten Objekt. Das theoretisch im
Vorhinein zu machen ist für mich schwierig, weil ich nicht weiss, welche
Eingangssequenz die "gefährlichste" ist.