GCC Preprocessor Capabilitiy?

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greetings -

 

Don't have access to AS7 for a while so I can't do the simple "try it and see if it works". I need to implement a DSP filter for 3 axes of accelerometer data. The starting point for the filter coefficients is:

 

X = 256 * exp( -2 * PI * Fcorner )

The resulting value is a run-time constant so it would be really nice to be able to do something like:

 

#define FCORNER   0.01      //corner frequency in Hertz
#define PI  3.1415
#define XFIL 256*exp(-2*PI*FCORNER)

BUT, I suspect that exp() is only available via AVR-libc and so is not available to the preprocessor. I can always just do

 

#define XFIL 35     //coefficient for 0.01Hz corner

So, the question is whether or not the preprocessor is capable doing the computation in the first example, or is it necessary to resort to the second one?

 

Thanks for your help!

Jim

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jul 1, 2019 - 06:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the only time the preprocessor itself does any arithmetic is if you have

#if expression

where expression will be an integer constant expression that is actually evaluated by the preprocessor.

 

Otherwise, when you have somethig like

#define X (10 + 30 - 50)

int x = X;

the preprocessor isn't doing any maths, it's simple text replacement into the code wherever you use X

int x = (10 + 30 - 50);

and the compiler will then evaluate it.

The compiler will  work out that  it's a constant expression with value -10.

But you can't have a constant expression that includes a function call, in that case it will have to be evaluated at runtime by calling exp().

 

So I think you are stuck with calculating the value yourself, like in your second example.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can precompute the value with an external tool, auto-generated a .h, all handled by the Makefile. The external tool can simply be a host-based C program, or python, or...

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I thought the preprocessor was"dumb"  in that it only treat this as text---it does not "understand" any data types.

 

bty the way, whyt type of filter are you trying to create?

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Joey: basically doing what you suggest.

 

avrcandies: Creating a high-pass filter to separate out relatively small acceleration changes from background gravity, in order to implement a "record trigger" function. The filtered data only drives the trigger algorithm and is never recorded. My reference is "The Scientist and Engineer's Guide to Digital Signal Processing" by Steven Smith, Chapter 19 (Recursive Filters). It is available on-line for free download, chapter by chapter. Highly recommend.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jul 1, 2019 - 07:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As others have pointed out the preprocessor only does text replacement. The compiler can then fold constants where possible.

 

If you were using C++11 or later you could use constexpr variables and functions to force compile time computation. This would require you to roll your own constexpr version of exp().

 

namespace Foo {

constexpr float exp( float arg ) noexcept
{
    // some implementation
}

constexpr auto FCORNER = 0.01;
constexpr auto PI      = 3.1415;
constexpr auto XFIL    = 256 * exp( -2 * PI * FCORNER );

} // Foo

 

github.com/apcountryman/build-avr-gcc: a script for building avr-gcc

github.com/apcountryman/toolchain-avr-gcc: a CMake toolchain for cross compiling for the Atmel AVR family of microcontrollers

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Really (really) short on code space. Going with #define numeric values.

 

Thanks

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is available on-line for free download, chapter by chapter. Highly recommend.

Thanks, wonderful!  Sad, since now I will spend some hours (that I'm lacking) wanting to read this book.  By the way, I don't think XFIL=35 is correct, I got some other value (256*0.939)...but perhaps I'm sleeping in the weeds.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is an online compiler where you can try out ideas when you don't have access to your own compiler-

https://godbolt.org/

 

I use it when I quickly want to try something out. Its usually not that important which compiler is chosen as long as its the same family (gcc). I usually just use the x86-64 compiler as you can see the printf output that way by turning on the a.out option.

 

This would require you to roll your own constexpr version of exp().

I'm not sure why you would need to roll your own. You can use the math.h version of exp(), and also use M_PI from math.h while you're at it. The compiler will certainly let you know when it cannot return a compile time value.

 

https://godbolt.org/z/9zm91b

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

I'm not sure why you would need to roll your own. You can use the math.h version of exp(), and also use M_PI from math.h while you're at it. The compiler will certainly let you know when it cannot return a compile time value.

 

https://godbolt.org/z/9zm91b

 

I did a quick test of what you posted (with a slight modification) using avr-gcc/avr-libc and it worked but it seems the compiler is able to do some strange/surprising stuff with standard library functions in constexpr contexts. Namely, avr-libc's math.h does not define exp() inline yet somehow it was still able to use it in a constexpr context. If I try to do something similar with a user defined function the compiler insists that it is declared constexpr and defined inline.

github.com/apcountryman/build-avr-gcc: a script for building avr-gcc

github.com/apcountryman/toolchain-avr-gcc: a CMake toolchain for cross compiling for the Atmel AVR family of microcontrollers

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, gcc has a large number of builtin functions, so they can all be used in constexpr.

https://gcc.gnu.org/onlinedocs/g...

 

I found out about this feature some time ago: https://www.avrfreaks.net/forum/...

 

This is a power far, far beyond what the preprocessor can do.

Last Edited: Mon. Jul 1, 2019 - 09:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

FYI - That number, XFIL=35, was picked randomly, out of the weeds. Only intended to show concept.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

Yes, gcc has a large number of builtin functions, so they can all be used in constexpr.

https://gcc.gnu.org/onlinedocs/g...

 

I found out about this feature some time ago: https://www.avrfreaks.net/forum/...

 

This is a power far, far beyond what the preprocessor can do.

 

I was not aware that gcc had that. Thanks for the info.

github.com/apcountryman/build-avr-gcc: a script for building avr-gcc

github.com/apcountryman/toolchain-avr-gcc: a CMake toolchain for cross compiling for the Atmel AVR family of microcontrollers

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

apcountryman wrote:
Namely, avr-libc's math.h does not define exp() inline yet somehow it was still able to use it in a constexpr context. If I try to do something similar with a user defined function the compiler insists that it is declared constexpr and defined inline.
The math library is part of the standard.

The compiler is allowed to know what exp does without

needing to look at the source or even the binary.

It can therefore make exp(10) into a compile-time constant.

 

One problem with this is that with math functions,

what the compiler knows can be wrong.

Different math libraries might get different values for exp(10).

 

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you don't have access to AS7, you can still play with the avr compiler at the command line (easy enough to get, unless you are for some reason tied to a work pc that is restricted, I suppose, or maybe you on a beach typing away on an ipad- but then why are you doing any math on a beach).

 

#include <math.h>

#define FCORNER 0.01
#define XFIL 256*exp(-2 * M_PI * FCORNER)
//or this
//#define XFIL 256*__builtin_exp(-2 * M_PI * FCORNER)

int main(){
    volatile int x = XFIL;
    for(;;){}
}

/*
 ./avr-gcc -Os test.c
 ./avr-size a.out

 text    data     bss     dec     hex filename
   20       0       0      26      14 a.out

 ./avr-objdump -d a.out

 Disassembly of section .text.startup:

00000000 <main>:
   0:   cf 93           push    r28
   2:   df 93           push    r29
   4:   00 d0           rcall   .+0             ; 0x6 <__zero_reg__+0x5>
   6:   cd b7           in      r28, 0x3d       ; 61
   8:   de b7           in      r29, 0x3e       ; 62

   a:   80 ef           ldi     r24, 0xF0       ; 240
   c:   90 e0           ldi     r25, 0x00       ; 0
   e:   9a 83           std     Y+2, r25        ; 0x02
  10:   89 83           std     Y+1, r24        ; 0x01

  12:   ff cf           rjmp    .-2             ; 0x12 <__zero_reg__+0x11>
*/

You can see the value (240) is loaded into the var x (on the stack), so you can use defines/macro in this case without having to pre-compute.

 

Yes, gcc has a large number of builtin functions

There is probably a lot more of that going on than we realize. The above example also works when using __builtin_exp() (don't even need math.h), so even when using what we think is the library version we get the built in version. 

Last Edited: Mon. Jul 1, 2019 - 11:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

You can see the value (240) is loaded into the var x (on the stack), so you can use defines/macro in this case without having to pre-compute.

Oh, so the gcc compiler is smarter than I thought!

After a bit of experimenting, If you compile with -fno-builtin, then it does produce code that includes the run time call to exp(), which is what I thought it would have to do in all cases, but clearly not.

If you are going to rely on the compiler not having to do the run time call, probably worth checking the generated code to make sure it has optimised.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

avrcandies wrote:

It is available on-line for free download, chapter by chapter. Highly recommend.

Thanks, wonderful!  Sad, since now I will spend some hours (that I'm lacking) wanting to read this book.  By the way, I don't think XFIL=35 is correct, I got some other value (256*0.939)...but perhaps I'm sleeping in the weeds.

 

There is also an older, complete version available for download from Analog Devices: https://www.analog.com/en/educat...

Last Edited: Tue. Jul 2, 2019 - 12:08 PM