Electronic – C++ AVR microcontroller optimization problem

attiny85avrccompileroptimization

I'm faced with a weird problem. I'm developing a program for AVR.

  • Microcontroller ATtiny85.
  • Frequency 1 MHz.
  • Programmer pickit2.
  • Software for programming avrdude on Linux.
  • Compiler avr-g++.
  • Optimization -Os (I tested with -O0, it doesn't work too).

This program must enable output pin PB3. But for some reason, it doesn't.

#include <avr/io.h>
#include <avr/interrupt.h>

#define NOINLINE_ENABLED 1

#if NOINLINE_ENABLED == 1
#define NOINLINE __attribute__ ((noinline))
#else
#define NOINLINE
#endif

// Error occurs only if SetLedState method has noinline attribute or function implemented in other .cpp file (also noinline).
// NOINLINE_ENABLED macros enables noinline for this method
void NOINLINE SetLedState(bool state) {
    if (state)
        PORTB |= (1 << PB3);
    else
        PORTB &= ~(1 << PB3);
}

class Test {
public:
    Test(unsigned int a) : a_(a) {}

    void Update() {
        // a_ for some reasons doesn't equal 5, but it's impossible.
        if (this->a_ == 5)
            SetLedState(true);
    }

private:
    // I don't know why, but error doesn't occur if I add first variable, which can never be used
    // unsigned int padding = 0;

    const unsigned int a_;
};

static Test *test;

ISR(TIMER1_OVF_vect) {
    // error occurs when method called from timer interrupt method
    test->Update();
}

[[noreturn]] int main() {
    test = new Test(5);

    DDRB = (1 << PB3);
    PORTB = 0;

    TIMSK = (1 << TOIE1);
    TCCR1 = (1 << CS10);

    sei();

    // no error occurs when method called from this place
    // test->Update();

    while (true);
}

I wrote some comments to explain why it doesn't work. I think the problem is in the optimizer.
Output PB3 doesn't enable if (and):

  • Test::Update method calls from timer overflow interrupt.
  • a_ class member has offset 2 and more.
  • SetLedState is no inlined or implemented in other *.cpp file.

Why?
Thanks in advance!

Edit:
Also, I tried to change the implementation of new C++ operator.
Earlier it called return alloca(size), but now I tried to call return malloc(size). And it worked. Also, this initialization works too:

static Test test_static;
test = &test_static;

Why it doesn't work with alloca? What is the difference with malloc and alloca AVR functions?

Best Answer

The reason(s) why this doesn't work for you could be

  • heap memory allocation doesn't work at all on your setup
  • the interrupt doesn't see the updated pointer because the pointer isn't volatile.

That said, I'd advise not to use the heap at all on such a small target. And why would you? You will know at build time which pins you want to trigger, so you can create the object(s) globally or local to main().

Also, why use an interrupt? If it is just to try it, OK, go ahead, but for beginners I'd stick to other solutions. Check the FSM (Finite State Machine) style.

Coding for such a small system in C++ is IMO the good way, but be aware that it is not like coding for larger systems. You will have to know very well what you are doing, and you will find lots of C++ examples and advice that might be pefect for largere systems, but not for an AVR8.