These forums are read only, please use our new forums here.

Main :: ToneCore Development Kit



FIR filter: how to use memory?
by chaosStrings on 2011-06-04 14:45:29

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

  1. set the accumulator to 0
  2. write 1024 times for each i=1 to n
  • store input value y(i) into y(i-1)
  • multiply y(i)*coefficient(i) and add to accumulator

It seems a pretty bulky thing to do ... I hope there is a more elegant and effective way. I thought it could be

  1. store all coefficient values in a 1024 table
  2. allocate a vector with 1024 values in which to store all the input values for the current and previous timesteps

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



Re: FIR filter: how to use memory?
by RedPandaCurt on 2011-06-05 06:19:46

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

.....

You need to be careful about using too much program memory - your global data section will get truncated without warning.  (Note that the DSP56k assembler allows you to initialize the coefficients directly in program memory (dc), but it won't work with the TCDDK.)
Then in your initialization code, copy it to an address in Y memory:

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



Re: FIR filter: how to use memory?
by chaosStrings on 2011-06-14 09:01:38

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).



Re: FIR filter: how to use memory?
by chaosStrings on 2011-06-16 08:32:48

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



Re: FIR filter: how to use memory?
by RedPandaCurt on 2011-06-17 19:10:38

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.



Re: FIR filter: how to use memory?
by mjkirk12 on 2013-02-25 10:29:50

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




The information above may not be current, and you should direct questions to the current forum or review the manual.