"High-End Audio" IIR filters

XCore Project reviews, ideas, videos and proposals.
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

I found my results of my testing of the 48 bit IIR's. This example is an IIR filter of many ten's of order. All measurements were from a digital loopback with full accuracy (full 24 bits resolution with no operating system issues). No analog at all.
The Noise level is:
IIR Hi-Res Noise Level.png
IIR Hi-Res Noise Level.png (4.29 KiB) Viewed 4305 times
IIR Hi-Res Noise Level.png
IIR Hi-Res Noise Level.png (4.29 KiB) Viewed 4305 times
Here's the THD:
IIR Hi-Res THD.png
IIR Hi-Res THD.png (4.89 KiB) Viewed 4305 times
IIR Hi-Res THD.png
IIR Hi-Res THD.png (4.89 KiB) Viewed 4305 times
The 48 bit filter shows basically no harmonic distortions. The noise margin is +10dB for the 48 bit.

But both 32 bit and 48 bit are quite good.

Edit: to highlight differences.
Last edited by bearcat on Sun May 13, 2012 2:05 am, edited 2 times in total.


bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

My code performs no sign testing.
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am
Contact:

Post by segher »

lilltroll wrote:Bearcat, do you use 2-comp. longmult or do test the sign and then branch to unsigned longmult ?
You can calculate everything as unsigned always, by biasing all
numbers (by 0x8000_0000_0000_0000). You'll end up with an
accumulator that is biased by some constant number (because
your filter coefficients are fixed), so just add its negative at the
end. You can totally ignore overflow, all of this is integer after
all :-)
bearcat
Respected Member
Posts: 283
Joined: Fri Mar 19, 2010 4:49 am

Post by bearcat »

I do plan on using the 48bit filters for subwoofer processing and crossovers, unlike the application shown above which I had written the filter for. Limit cycles are certainly an issue down there. I plan on performing similiar measurements in the future when I actually use the 48 bit version.
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

segher wrote:
lilltroll wrote:Bearcat, do you use 2-comp. longmult or do test the sign and then branch to unsigned longmult ?
You can calculate everything as unsigned always, by biasing all
numbers (by 0x8000_0000_0000_0000). You'll end up with an
accumulator that is biased by some constant number (because
your filter coefficients are fixed), so just add its negative at the
end. You can totally ignore overflow, all of this is integer after
all :-)
Do you mean like this ?
int64y(n) is the signal
int32A is the signed filter coeff that is only changed every now and then, but not every sample
k=0x8000_0000_0000_0000 is the offset
hi:mi:lo is the result as a int96

If(A>=0)
offset= -k*A is precalculated when the filter coef. is updated
(y(n)+k)*A + offset = A*y(n)+k*A - k*A = A*y(n)
end

if(A<0)
offset = k*|A| is precalculated when the filter coef. is updated
(y(n)+k)*(-|A|) + offset = -|A|*y(n) - k*|A|+k*|A|=-(|A|*y(n))
end
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am
Contact:

Post by segher »

Yes, something like that. It saves instructions because the correction
it has to do at the end is always the same, not dependent on the sign
bit of things; so a) it does not have to calculate with the sign bits, and
b) some expressions can be combined in the end.

Even easier of course is to use the MACCUS instruction, which
unfortunately does not exist ;-)
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

segher wrote:Yes, something like that. It saves instructions because the correction
it has to do at the end is always the same, not dependent on the sign
bit of things; so a) it does not have to calculate with the sign bits, and
b) some expressions can be combined in the end.
The 96-bit result exists in hi:mi:lo

Even easier of course is to use the MACCUS instruction, which
unfortunately does not exist ;-)
ASM-master segher, I might need some helt with negative coeffs.

For positive coeffs, you can do it like this, and with only one coef. A as in this example, the Offset Ohi:Omi can be pre-negated and replace the zero's in the lmul avoiding the lsub in the end.
Insignal: Yhi:Ylo
Coeff: A
Result: hi:mi:lo

Code: Select all

POS:
lmul Ohi,Omi,A,offset,zero,zero // Calculate the offset
POSloop:
add Yhi,Yhi,offset //add offset to in-signal
lmul carry, lo, Ylo, A, zero, zero
lmul hi, mi, Yhi, A, zero, carry
//Calc 2-comp. value by subtracting the offset.
lsub borrow,hi,hi,Ohi,zero
lsub s0,mi,mi,Omi,borrow
sub lo,lo,borrow
But what about the negative case ? Can I avoid the negate at the end.

Code: Select all

NEG:
lmul Ohi,Omi,A,offset,zero,zero
NEGloop:
add Yhi,Yhi,offset //add offset to in-signal
lmul carry, lo, Ylo, A, zero, zero
lmul hi, mi, Yhi, A, zero, carry
//Calc 2-comp.
lsub borrow,hi,Ohi,hi,zero
lsub borrow,mi,Omi,mi,borrow
sub lo,lo,borrow
//negate hi:mi:lo
not hi,hi
not mi,mi
neg lo,lo
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am
Contact:

Post by segher »

Your "negate" at the end is incorrect, it does not carry.

You're doing Ox-x, and then negating it; you can just do x-Ox
instead.

If you do things right, there is no difference whatsoever between
positive and negative numbers, which is the point. By adding the
offset, you're shifting the [-80000000..7fffffff] range to [0..ffffffff],
which is much nicer to compute with. Say for example you are
jut doing a*b, and write D=80000000, then you instead compute
(a+D)*(b+D) which is a*b+(a+b+D)*D, so you get a*b by subtracting
the right side of that (which is cheaper than it looks!)

Nothing in there is dependent on sign, it works exactly the same for
positive and negative numbers, which is the point :-)
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

If you look at the expression above, "Ox-x" is correct, the incorrect thing is the order of lsub together with the borrow.

I believe this has a chance of beeing correct, and it is "Ox-x" all the way.

Code: Select all

NEGloop:
add Yhi,Yhi,offset //add offset to in-signal
lmul s0, s2, Ylo, A, zero, zero
lmul hi, mi, Yhi, A, zero, s0
lsub s0,lo,zero,s2,zero
lsub s0,mi,Omi,mi,s0
lsub s0,hi,Ohi,hi,s0
Thank's anyway
User avatar
lilltroll
XCore Expert
Posts: 956
Joined: Fri Dec 11, 2009 3:53 am
Location: Sweden, Eskilstuna

Post by lilltroll »

segher wrote:Your "negate" at the end is incorrect, it does not carry.

You're doing Ox-x, and then negating it; you can just do x-Ox
instead.

If you do things right, there is no difference whatsoever between
positive and negative numbers, which is the point. By adding the
offset, you're shifting the [-80000000..7fffffff] range to [0..ffffffff],
which is much nicer to compute with. Say for example you are
jut doing a*b, and write D=80000000, then you instead compute
(a+D)*(b+D) which is a*b+(a+b+D)*D, so you get a*b by subtracting
the right side of that (which is cheaper than it looks!)

Nothing in there is dependent on sign, it works exactly the same for
positive and negative numbers, which is the point :-)
The thing is that multiplication works exactly the same for
positive and negative numbers as well, as long as you sign extend the numbers.

The question is thus, when is it cheaper to add an offset and subtract it on the XMOS ISA, compared to just multiply direct?
Assume that both a and b are a(n) and b(n), (time-dependent), is it better to use the bias method? Or is it only when one coefficient becomes static over a longer time-window the total instruction count is reduced.
Post Reply