5
\$\begingroup\$

I'm currently writing some of my first assembly code for a new project, I am applying a small non-recursive filter algorithm to some saw tooth wave data held in memory, in order to blunt the edges.

Using thumb2 instruction set on a cortex m3.

The algorithm being used is:

y[0] = x[-2]/8 + x[-1]/8 + x[0]/4 + x[1]/8 + x[2]/8

I have spent a long time looking at my code but cannot seem to optimize it any further. The Assembly code I have written is in a loop for the length of data and I have applied the algorithm as so:

  LDMIA r0,{r5-r9}     ; get the next 5 data values to be filtered
  ADD r5,r5,r9         ; sum x[-2] with x[2]
  ADD r6,r6,r8         ; sum x[-1] with x[1]
  ADD r9,r5,r6         ; sum x[-2]+x[2] with x[-1]+x[1]
  ADD r7,r7,r9,LSR #1  ; sum x[0] with (x[-2]+x[2]+x[-1]+x[1])/2
  MOV r7,r7,LSR #2     ; form (x[0] + (x[-2]+x[-1]+x[1]+x[2])/2)/4
  STR r7,[r3],#4       ; save calculated filtered value
  ADD r0,r0,#4         ; move pointer address forward
  SUBS r4,r4,#1        ; decrement loop counter

Can any one see anywhere I could have better optimized this?

Full Code if needed

\$\endgroup\$
9
  • \$\begingroup\$ You could pre-divide the whole input by 4, but that would possibly be even slower, since you're not "really" dividing but just shifting. I read that there are SIMD extensions for ARM, so maybe that's something you want to look into, \$\endgroup\$
    – kyrill
    Commented Apr 13, 2017 at 10:46
  • 1
    \$\begingroup\$ Which assembly language? There are many assembly languages for many different types of processors. \$\endgroup\$
    – Edward
    Commented Apr 13, 2017 at 11:04
  • \$\begingroup\$ Sorry for the lack of info there, it's thumb2 on a cortex m3! Cheers! Also I have had a look at the simd instructions available for this instruction set and they don't seem relevant for this task, but I am a beginner, so every chance I may be wrong! \$\endgroup\$
    – ohkneel
    Commented Apr 13, 2017 at 11:06
  • 2
    \$\begingroup\$ @ratchetfreak: SIMD instructions aren't available on the M3. Only M4 and above. \$\endgroup\$
    – Edward
    Commented Apr 13, 2017 at 15:56
  • 1
    \$\begingroup\$ Both the original and the accepted answer have numerical limitations. For example, calculate what happens if the values are all equal to 7 and determine what behavior you desire. Also note that even a steady state input will be attenuated. I assume this is intended. \$\endgroup\$
    – Edward
    Commented Apr 13, 2017 at 20:23

1 Answer 1

4
\$\begingroup\$

You need to realize that as you are passing over the input, you will be dividing each input by 8 four times and dividing each input by 4 once. You will also calculate the sum of each adjacent input twice. We can try to get rid of this redundancy.

First lets rewrite the filter:

y[0] = x[-2]/8 + x[-1]/8 + x[0]/4 + x[1]/8 + x[2]/8

like so:

y[0] = (x[-2] + x[-1] + x[0] + x[1] + x[2])/8 + x[0]/8 + (1 if x[0] odd, 0 otherwise)

Multiply both sides by 8:
8*y[0] = (x[-2] + x[-1] + x[0] + x[1] + x[2]) + x[0] + 8*(x[0]&1)

Assuming integers, if the input is floating point, just skipp the odd/even bit.

Notice how you now have a sliding sum? That's effective to compute.

I'm not well versed in assembly but I can give you pseudo C-code which you can translate to assembly:

void computeSample(int slidingSum, int midSample){
   return (slidingSum + midSample + (midSample & 1) * 8)/8;
}

void filter(int *input, int *output, int len) {
    const int width = 2;

    if (len < width*2 + 1) { return; }

    int slidingSum = 0;
    for (int i = 0; i < width*2 + 1; ++i) {
        slidingSum += input[i];
    }

    output[width] = computeSample(slidingSum, input[width]);
    for (int x0 = width + 1; x0 < len - width; ++x0) {
        slidingSum -= input[x0 - (width+1)];
        slidingSum += input[x0 + width];
        output[x0] = computeSample(slidingSum, input[x0]);
    }
}

You need to figure out how to handle the edges of the signal (repeating, zero extending, no-data etc).

Hope this helps!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ @kyrill I fixed an issue with now you calculated output[width] also the filter only works for width=2 as the division constant depends on the filter length to maintain the same level. \$\endgroup\$
    – Emily L.
    Commented Apr 13, 2017 at 16:02
  • \$\begingroup\$ I see. Don't know which is better -- to hardcode 2, 3, etc. or to pretend that the width is variable. \$\endgroup\$
    – kyrill
    Commented Apr 13, 2017 at 18:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.