avr-g++ treats SFRs differently re constexpr in v 5.4 v/s 7.2

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

[note this started life on tutorial thread about "toolchain flavours"]

 

Thanks, I've added avr-gcc 7.2 as a "flavour". Naturally, weirdness ensued.

Using the old version (adding the -std=gnu++14 option) this compiles fine (just a minimal example):

 

#include <avr/io.h>

constexpr int test = (int) &PORTD;

int main() {
	return test;
}

 

But with 7.2 it gives an error:

Error        'reinterpret_cast<volatile uint8_t* {aka volatile unsigned char*}>(43)' is not a constant expression

So either the old version is not complying with some pedantic C++ standard, or the new one has a bug. IMO, 43 is just a number, so what's the problem? Why wouldn't constexpr work?

Last Edited: Mon. Oct 23, 2017 - 12:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Off the top of my head - no idea. And ability (not on Windoze) or time to experiment (or even think) right now. I'm off to bead - 23:30 here and tomorrow is monday..

 

How fares

#include <avr/io.h>

constexpr int test = 43;

int main() {
	return test;
}

?

 

If that's OK, then I suspect something involving either the pointer or the volatile to cause it. 

 

Experiment yourself, removing PORTD replacing it with combinations involving 43, volatile and the pointer to see if you come closer..

 

Depending on how tomorrow fares with me, I'll be back here in the evening. Good luck, and let us know!

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Sun. Oct 22, 2017 - 09:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The problem seems to be the pointer, but I can't understand why it was ok in the older version and no longer ok in gcc 7.2... Anyway, it's not really important.

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

can it be becaues "PORTD" is not a normal number but a special number. IIRC if you look in the io.h it is a "special function register" pointer. Might be that with converting these to 'normal' int type you get this error.

 

edit:

 

and also PORTD can be probably 2 numbers one being with an offset for use in some operational cases and sometimes without an offset. might be that there is a choice problem in this case.

Last Edited: Mon. Oct 23, 2017 - 06:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

meslomp wrote:
can it be becaues "PORTD" is not a normal number but a special number. IIRC if you look in the io.h it is a "special function register" pointer. Might be that with converting these to 'normal' int type you get this error.   edit:   and also PORTD can be probably 2 numbers one being with an offset for use in some operational cases and sometimes without an offset. might be that there is a choice problem in this case.

 

No(ish), and no.

 

PORTD unambigously expands to

(*(volatile uint8_t *)((0x0B) + 0x20));

So the "number" as such is 0x28.

 

For the "not a nurmal number" I'd be more comfortable with something like "being of a specific type": The integer literal is cast (by the __SFR_IO macro) to a pointer to a volatile uint8_t (i.e. a pointer to the port location (in memory-mapped I/O space), and the contents of that location is volatile). Nothing special except that.The last bit of the PORTB macro expansion is that this pointer is then de-referenced. I.e. the type you actually end up with when using PORTB is a volatile uint8_t.

 

ElTangas then takes the address of this.

 

 Note that this whole circus is done in the C preprocessor, and what the compiler actually sees when El Tangas codes

 

constexpr int test = (int) &PORTD;

is

constexpr int test = (int) &(*(volatile uint8_t *)((0x0B) + 0x20));

which is equivalent to

constexpr int test = (int) &(*(volatile unsigned char *)0x2B);

I.e., the type of the expression PORTB is simply volatile uint8_t (equivalent to volatile unsigned char). Nothing "special", apart from the macro expansion circus. 

 


 

@El Tangas. You only said "the old version [of the compiler]" [1]. I've tested with avr-g++ 3.5.4 and that version does not generate the error.  I don't even know how to get my hands on a (reliable [2]) 7.2 for GNU/Linux, let alone for Windows... Where did you get yours?

 


[1] Why not state the version rather than "the old version"? Just do avr-gcc --version on a command prompt to get it.

[2] No, I don't trust the distribution repos for this..

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Mon. Oct 23, 2017 - 08:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, let me be more specific.

Old version (installed with AS7):

C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin>.\avr-gcc --version
avr-gcc (AVR_8_bit_GNU_Toolchain_3.6.0_1734) 5.4.0

New version (obtained from http://blog.zakkemble.co.uk/avr-... ):

C:\Program Files (x86)\Arduino\hardware\tools\avr\bin>.\avr-gcc --version
avr-gcc (GCC) 7.2.0

I don't know how reliable it is, but it works well most of the time.

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

Interesting! I see his build script runs on Linux (Ubuntu/Debian stated, so it might very well work on my Mint) and builds a Linux-targeted tool chain as a first step. Might try it if I find the time later this week..

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Johan, thanks for this explanation.

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

El Tangas wrote:
So either the old version is not complying with some pedantic C++ standard, or the new one has a bug.
IMHO it is the former.

A C++ compiler transfers a C-style cast into a C++ equivalent according to a prioritizing list (const_cast, static_cast, reinterpret_cast). An int to pointer cast becomes a reinterpret_cast, and reinterpret_casts in general are not allowed within constexpr.

Stefan Ernst

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

Yes, unfortunately I had the same feeling, but was hopping to be wrong, since that translates to less code flexibility.

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

Hey, El Tangas!

 

I pulled the build script you pointed to and just got a build on Linux done. Alas, it's too late to test it out, but hopefully tomorrow. Thank you for the link!

 

(But why-oh-why do I allow myself to be pulled into all this cool stuff all the time. I should get back to the debugging stuff...)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:
(But why-oh-why do I allow myself to be pulled into all this cool stuff all the time. I should get back to the debugging stuff...)

 

I guess you're just human. Nothing wrong with that... yet.

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

A C++ compiler transfers a C-style cast into a C++ equivalent according to a prioritizing list (const_cast, static_cast, reinterpret_cast). An int to pointer cast becomes a reinterpret_cast, and reinterpret_casts in general are not allowed within constexpr.

 Is there a way to achieve the same results?

For a time where some people are really encouraging the use of C++ in embedded systems, along with "better things than #define", it seems that other people are really making it difficult to do "commonly necessary" things.   I mean, there's a whole CMSIS standard for ARM that it seems to me says that makes ioregister definitions unusable by constexpr...

 

(I guess there's the Microchip PIC32 solution, where peripheral base addresses are defined at link time, but I didn't like that very much...)

 

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

I don't know if there is a way...

 

The best I could ever achieve was being able to use SFRs in templates, by defining the SFRs using the avr-gcc extension that allows issuing a fixed address to a variable.

 

So, for example, if I define PINB like this:

volatile uint8_t PINB __attribute__((io (0x23), weak));

 

...instead of the usual way that casts an integer, this code becomes valid:

#include <stdint.h>

volatile uint8_t PINB __attribute__((io (0x23), weak));

template <volatile uint8_t & SFR>
const int SFR_Address = (int) &SFR;

int main() {
	return SFR_Address<PINB>;
}

 

Normally you can't do this, the compiler cries there are "side-effects", whatever that means. (This code just returns the address of the SFR used as template argument)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
//  an ugly workaround

constexpr int test=43;
static int testok=(test==(int)&PORTD);

int main()
{
    // compilation failure if testok not known true
    if(!testok) asm(" .error  not testok" ::);
    return test;  // not good for embedded
}

 

Iluvatar is the better part of Valar.

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

skeeve wrote:
// an ugly workaround constexpr int test=43; static int testok=(test==(int)&PORTD); int main() { // compilation failure if testok not known true if(!testok) asm(" .error not testok" ::); return test; // not good for embedded }

 

Still ugly, but at least without asm:

 

#include <avr/io.h>

constexpr int test=43;
static const int testok=(test==(int)&PORTD);
static_assert (testok, "not testok");

int main()
{
	return test;  // not good for embedded
}

 

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

If static_assert works, use it.

Does it compile?

I deliberately did not use const on testok.

 

Macro-izing the if shouldn't be too hard.

Neither should porting it to other platforms that allow inline assembly.

Iluvatar is the better part of Valar.

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

skeeve wrote:
I deliberately did not use const on testok.

const is required by static_assert

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

The one with static_assert gives errors in gcc 7.2:

 

main.cpp(42,1): error: non-constant condition for static assertion
		 static_assert (testok, "not testok");
		 ^~~~~~~~~~~~~
main.cpp(42,1): error: the value of 'testok' is not usable in a constant expression
main.cpp(41,18): info: 'testok' was not initialized with a constant expression
		 static const int testok=(test==(int)&PORTD);
		                  ^~~~~~

 

The one with asm never gives an error, even in 7.2. The condition

(test==(int)&PORTD)

is always true. There was also a small error, you need to use escaped quotes:

	if(!testok) asm(" .error  \"not testok\"" ::);

 

The problem is not as simple as it seems. If you want to explore this, just get 7.2 and see for yourselves.

Last Edited: Wed. Oct 25, 2017 - 10:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

The one with static_assert gives errors in gcc 7.2:

 

main.cpp(42,1): error: non-constant condition for static assertion
		 static_assert (testok, "not testok");
		 ^~~~~~~~~~~~~
main.cpp(42,1): error: the value of 'testok' is not usable in a constant expression
main.cpp(41,18): info: 'testok' was not initialized with a constant expression
		 static const int testok=(test==(int)&PORTD);
		                  ^~~~~~

Indeed. I verified this, but not with GCC 7.2.

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

In case 'tis not obvious, the asm requires enough optimization for it to be dead code removed.

Iluvatar is the better part of Valar.