PROGMEM on avr-gcc 5.4.0

This is not a question; merely an observation. I have a font table in progmem:

static const uint8_t PROGMEM font[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 
…
        0x00, 0x00, 0x5F, 0x00, 0x00,            
        0x00, 0x03, 0x00, 0x03, 0x00,            
  
… many bytes ... 
};

and read it using pgm_read_byte()

uint16_t line_idx = font + char_idx + col;
uint8_t line = pgm_read_byte(line_idx);

except that it doesn't work. It doesn't work unless I give the compiler a nudge. If I add one line like this:

void setup() {
…
if (millis() > 100000L) Serial.println((uint16_t) font, HEX); 
…
}

then things work. Note the Serial.println never gets called, but the compiler doesn't know that.

When disassembling, the code that works contains the font table in .text, where you'd expect it:

Disassembly of section .text:
00000000 <__vectors>:
…
00000068 <__trampolines_end>:
…
0000020b <_ZL4font>:
        ...
     2af:       00 00 00 5f 00 00 00 03 00 03 00 14 7f 14 7f 14     ..._............

When I disassemble the code that doesn't work the bytes are there, but they're marked as executeable code, not as data.

Disassembly of section .text:
00000000 <__vectors>:
…
00000068 <__trampolines_end>:
…
     10c:       00 00           nop
     10e:       00 5f           subi    r16, 0xF0       ; 240
     110:       00 00           nop
     112:       00 03           mulsu   r16, r16
     114:       00 03           mulsu   r16, r16
     116:       00 14           cp      r0, r0
     118:       7f 14           cp      r7, r15
     11a:       7f 14           cp      r7, r15

Rest of the sketch is on github . Arduino 1.8.9, avr-gcc 5.4.0. Hope this helps someone.

koendv:
except that it doesn't work.

Could you be more specific about what you mean by "doesn't work"?

OK. I'll try to explain. The sketch contains two pieces of code that use progmem: the display and ps2keyboard. The display uses progmem to store font bitmaps, and ps2Keyboard uses progmem to store keyboard character maps. The errors in the display would occur whenever display or keyboard tried to access progmem. Just for testing, I tried all combinations of storing (small) font bitmaps in ram or progmem and enabling or commenting out ps2keyboard::loop().

Store font bitmap in progmem, run ps2keyboard::loop(): display is garbage.
Store font bitmap in progmem, comment out ps2keyboard::loop(): display is garbage.
Store font bitmap in ram, run ps2keyboard::loop(): display is garbage.
Store font bitmap in ram, comment out ps2keyboard::loop(): display is fine.

But: store font bitmap in progmem, run ps2keyboard::loop(), and add the line:

if (millis() > 100000L) Serial.println((uint16_t) font, HEX);

to setup() and suddenly everything works, including keyboard. The if test is always false of course, and the Serial.println() is never going to be executed, because setup() is not going to take 100 seconds to run. But somehow this line causes the compiler to generate code that needs to know the begin address of the font array. The code around pgm_read_byte() that leads up to the lpm instruction stays the same, what changes is that the elf now has a "font" symbol entry. Does this answer your question?

Board?

atmega328p/Arduino Uno.

In the attachment, retroleds.ino.elf.objdump is the sketch, compiled and disassembled "as is". Font data begins at address 0x20b.

In retroleds/with_line_373_commented_out/retroleds.ino.elf.objdump is the sketch, compiled and disassembled with the line "if (millis() > 100000L) Serial.println((uint16_t) font, HEX);" commented out. After address 0x68 there's the same font data, but this time the font data is marked as code. Correct me if I'm wrong.

retroleds.zip (158 KB)

Ah. I get it. Your code (without the println) does not have an actual reference to the data contained within font so the optimizer correctly removes it.

I wonder how I solved that problem... Ugh. I can't find it. I'll try later.

Try this...

uint8_t line = pgm_read_byte( & font [ char_idx + col ] );

Odd thing is that, going by the amount of program memory used by the sketch, the array appears to be in progmem either way.

Well, I could be wrong.

Unfortunately there isn't an easy way to duplicate the problem without the specific hardware being used, since inserting a print statement for the array data apparently fixes the error.

david_2018:
Unfortunately there isn't an easy way to duplicate the problem without the specific hardware being used, since inserting a print statement for the array data apparently fixes the error.

Is it possible to serial.print the memory addresses? I can't easily see it, but problems like this sometimes point to (no pun) buffer or array overruns.