Jun 4, 2011 2:45 PM
FIR filter: how to use memory?
-
Like (0)
Hi all!
I recently bought a TCDDK, and I'm having a bit of trouble in programming ...
My goal is to implement a FIR filter (1024 tap) for acoustic guitar simulation. I could do something like this:
0. declare 1024 variables for the data at current and previous timesteps y(n), y(n-1), y(n-2) etc
and in the program
It seems a pretty bulky thing to do ... I hope there is a more elegant and effective way. I thought it could be
and finally multiply member by member with a repeat instruction, something like
REP #1024
mac x0,y0,a x:(i),x0 y:(i),y0
and i should increment automatically ...
Still, I haven't understood yet how to deal with memory allocation and indexing with vectors or tables ... hence my questions:
- how do I allocate a vector with a specified length?
- how do I store some given coefficients in such vector? and, for the input data, how can I save both the current input and the previous values at each timestep?
- what index should I use in order to access the data?
I hope my questions are clear, thank you in advance for your answers!
Daniele
IIRC, you will only be able to 512 taps using MACs with X/Y memory and modulo addressing, because you need to store the coefficients in program memory for initialization. I describe that approach below, but you also might want to read the coefficients directly from program memory in your FIR (see the "Pitch detection/octave effect" thread for ideas).
You can allocate arrays in X and Y memory like the following. For modulo-1024 addressing, each array needs to start at a multiple of 1024 within its address space. The simplest way to do that is to put them at the beginning of the section.
org x:$000
fir_state ds 1024
org y:$000
fir_coeffs ds 1024
For initializing tables in X or Y memory from program memory, you can define a global data section after the rest of your code:
section P_DATA_MEMORY global
org p:
; Table values
COEFF_0 dc $000000
COEFF_1 dc $000001
.....
move #COEFF_0,r0 ; address in program memory
move #fir_coeffs,r4 ; address in Y memory
do #1024,init_coeffs_end ; repeat for number of entries
move p:(r0)+,y0
move y0,x:(r4)+
init_coeffs_end:
Also, don't forget to clear the filter state.
Use post-increment addressing to increment the addresses in your FIR loop. The DSP56K has a modulo addressing mode to implement the delay line; each Rn register has an Mn register that you can set to modulus-1. See Section 4.5.3 of the DSP56300 Family Manual for more details. You can find examples of DSP56K FIRs on the web, but the basic idea is something like this (I didn't assemble/test this):
; once during initialization:
move #fir_state,r0 ; state
move #1023,m0 ; mod(1024)
move #fir_coeffs,r4 ; coefficients
move #1023,m4 ; mod(1024)
; for each sample:
move y:LeftInput,x0 ; input sample
; FIR
clr a x0,x:(r0)+ y:(r4)+,y0
rep 1023
mac x0,y0,a x:(r0)+,x0 y:(r4)+,y0
macr x0,y0,a (r0)- ; round and shift input samples
move a,y:RightOutput ; output sample
Hi!
Many thanks for the answer! I finally managed to code a FIR filter (100 taps) with a windowing function. The results from frquency plots seem good ... it works !!! ![]()
I will post the code (I made some slight modifications to your suggestions) soon. Now I will try to find out how to increase the number of words which can be sent to the pedal (the maximum seems to be the size of the 2 band EQ program ...). It seems I will have to reflash the MCU ...
Furthermore, I am checking how to downsample a .wav file, in order to compute the coefficients for the 39.0625 kHz sample frequency from standard impulse responses (44.1 kHz).
Hi! I tried to expand the memory for a 512 tap filter by reflashing the MCU module, but I am experiencing problems ... I think that not all the coefficients are sent ... This is my code:
page 132,60
include "..\Ioequ.inc"
include "..\vectors4.inc"
list
n equ 100 ; number of filter taps
then the constants are defined for the knobs, footswitches etc. The memory and coefficients are defined as follows:
org x:$100 ;make equal to number of taps!!!!
fir_state ds n
;**********************************************
; Write your internal Y-memory allocations here
;**********************************************
org y:$000
fir_coeffs ds n
During the initialization I clean the memory for the filter state and the coefficients (even if this last is not really needed)
; Initialize registers
move #>$000000,x0
move x0,r0
rep #28
move x0,x:(r0)+ ; clear start of x:mem
move #fir_state,r0
rep #n
move x0,x:(r0)+ ; clear start of x:mem
;move #fir_coeffs,r0
;rep #n
;move x0,y:(r0)+ ; clear start of x:mem
and I load the coefficients with the method explained by RedPandaCurt (the coefficients are defined at the end of the program, as previously explained)
move #COEFF_0,r0 ; address in program memory
move #fir_coeffs,r4 ; address in Y memory
do #n,init_coeffs_end ; repeat for number of entries
move p:(r0)+,y0
move y0,y:(r4)+
init_coeffs_end:
; once during initialization:
move #fir_state,r0 ; state
move #n-1,m0 ; mod(n)
move #fir_coeffs,r4 ; coefficients
move #n-1,m4 ; mod(n)
In the even interrupt section I do the following:
;*****************************************************************************************
; Do the processing here! x0 contains the left channel sample, x1 the right channel sample
;*****************************************************************************************
movep x:M_RX0,x:(r0)
; FIR
clr a x:(r0)+,x0 y:(r4)+,y0
rep #n-1
mac x0,y0,a x:(r0)+,x0 y:(r4)+,y0
macr x0,y0,a (r0)- ; round and shift input samples
move a,x0 ; output sample
; The left channel sample to be outputted
move x0,x:LeftTx
; The right channel sample to be outputted
move x0,x:RightTx
;*******************
; End of processing.
;*******************
The program, as said, works for 100 taps. When I increase the number, and reflash the MCU according to the instruction in the TCDK user guide, it seems some of the coefficients are not loaded ... even if I have got roughly 700 words, much less than the 1024 allowed. Any suggestions for this? (I really really need those 512 taps ...)
Tank you for your answer,
Daniele
One problem I see is that fir_state is starting at $100. For modulo 512 addressing, the base address needs to be a multiple of 2^k. For 512 taps, k=9, so the starting address should be $000 or $200. (For 100 taps, k=7 so 2^k=128 and $100 (256 decimal) is a multiple of that).
You can verify that all of your coefficients are loaded by copying the last coefficient from y memory to one of the read-from-DSP debug registers.
If you are still working on this...
For linear phase FIR filters (typical application), the impulse response (coefficients) must be
symmetric: h[n] = h[N-n-1] e.g. h[0] = h[1023], h[1] = h[1022], etc.
or
anti-symmetric: h[n] = -h[N-n-1]
For a 1024 tap filter, you really only need to store 512 coefficients. With proper addressing (modulo pointers), you can do 2 MAC operations with the same coefficient, looping 512 times. When the # of coefficients is odd, you need to handle the middle coefficient as a special case, outside the filter loop at the end.
With proper design, an IIR (recursive) filter can be used to approximate a long FIR filter, maybe only requiring 3 or 4 coefficients.
Or the DSP could be used to compute the coefficients dynamically at boot time storing them in X or Y data memory.
Then you would not need to download them in the code image.
Cheers,
Mike
Stay in the mix and in the know.
Latest offers, special deals and insider updates.