Electronic – Using a variable as array index – Optimization Bug

atmegaavravr-gccoptimization

I discovered a bug in my code which only turns up if avr-gcc optimization is used.

Can somebody explain whats the problem here?

I'm aware that there are several smart ways to achieve some PWM, but that is not the point here.

Background Information

I play around with an LED and dimming it with PWM. To do some simple gamma correction I use a predefined array with the right values.

Code

This is the code I use, Main contains just the interrupt init and an empty while(1) loop:

volatile size_t fade;
volatile uint16_t counter = 0;

const uint16_t PROGMEM pwmtable_10[64] = {
    0, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10,
    11, 12, 13, 15, 17, 19, 21, 23, 26, 29, 32, 36, 40, 44, 49, 55,
    61, 68, 76, 85, 94, 105, 117, 131, 146, 162, 181, 202, 225, 250,
    279, 311, 346, 386, 430, 479, 534, 595, 663, 739, 824, 918, 1023
};

ISR(TIMER0_OVF_vect) {
 // fade is set in the main loop or somewhere else
    if(counter < pwmtable_10[fade]) {
        counter=counter+1;
        PORTB = 0;
    } else {
        counter=0;
        PORTB |= RED;
    }
}

A value of 1 gives a very bright light, a value of 1023 gives a very dimmed light. The value 1023 is accessible through the array index 63.

The Problem

It doesn't matter which value I set fade to, the LED is always bright, if I compile my code with avr-gcc -Os.
By using avr-gcc -O0 the code works.

Changing to code doesn't work even by setting fade to 63 inside the interrupt:

ISR(TIMER0_OVF_vect) {
    fade=63;
    if(counter < pwmtable_10[fade]) {
    //[..]

This is what works:

  • Replacing pwmtable_10[fade] with 1023.
  • Replacing pwmtable_10[fade] with pwmtable_10[63]
  • Replacing fade with a new variable directly declared before the compare

Additional Info

Platform: Atmega 168

% avr-gcc --version
avr-gcc (GCC) 4.8.2

Commands to build and flash the code:

avr-gcc -Wall -Wextra -Os -mmcu=atmega168 -DF_CPU=16000000 -o moody.elf moody.c
avr-objcopy -j .text -j .data -O ihex moody.elf moody.hex
avrdude -b4 -c usbasp -v -p m168 -P usb -U flash:w:moody.hex

Best Answer

I believe problem is in PROGMEM attribute which saves const array into flash memory. To get values you need a memcpy_P() or so. For direct access try avoiding PROGMEM attribute. pwmtable_10[fade] gets random RAM value.

See http://www.nongnu.org/avr-libc/user-manual/pgmspace.html