Question about PROGMEM and __flash: can I eliminate a step?

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

Hi all,

 

I have lots of bitmap files (character font data for a graphics VFD display) in PROGMEM as such:

#ifndef IBM_8X16_H
#define IBM_8X16_H

#define ibm_8x16 (pgm_get_far_address(_ibm_8x16)) // get address of font in flash

static const uint8_t _ibm_8x16[] PROGMEM = {

    0x08, // width (base + 0)
    0x10, // height (base + 1)
    0x00, // horizontal gap (base + 2)
    0x00, // vertical gap (base + 3)
    0x20, // first char (base + 4)
    0x7F, // last char (base + 5)
    0x10, // bytes per char (base + 6)

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20
    0x00, 0x00, 0x38, 0xFC, 0xFC, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0D, 0x00, 0x00, 0x00,
    0x00, 0x0E, 0x1E, 0x00, 0x00, 0x1E, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x20, 0xF8, 0xF8, 0x20, 0xF8, 0xF8, 0x20, 0x00, 0x02, 0x0F, 0x0F, 0x02, 0x0F, 0x0F, 0x02, 0x00,

    //////// snip ////////

    0x00, 0x00, 0x00, 0xBC, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00,
    0x00, 0x04, 0x04, 0xBC, 0xF8, 0x40, 0x40, 0x00, 0x00, 0x08, 0x08, 0x0F, 0x07, 0x00, 0x00, 0x00,
    0x08, 0x0C, 0x04, 0x0C, 0x08, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x80, 0xC0, 0x60, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x07, 0x07, 0x04, 0x04, 0x04, 0x07, 0x07, 0x00, // 0x7F
};

#endif

 

Notice that I access the font data by it's 24 bit address in PROGMEM using "pgm_get_far_address".  I've been looking at the "__flash" keyword and I *think* if I use it I will get the font address directly (without needing to use "pgm_get_far_address").

 

2 Questions:

 

(1) Am I correct in my assumption?

 

(2) How do I use the keyword in place of PROGMEM?

 

I've tried all different combinations of examples I've found and none seem to work.

 

BTW if it matters, I'm using avr-gcc 9.3.0 that I compiled from source:

 


root@michael:[1][/usr/share/arduino-1.0.6/libraries/Noritake_GUU100/fonts]# avr-gcc -v
Using built-in specs.
Reading specs from /opt/local/lib/gcc/avr/9.3.0/device-specs/specs-avr2
COLLECT_GCC=avr-gcc
COLLECT_LTO_WRAPPER=/opt/local/libexec/gcc/avr/9.3.0/lto-wrapper
Target: avr
Configured with: ../configure --build=x86_64-pc-linux-gnu --prefix=/opt/local --target=avr --enable-languages=c,c++ --disable-nls --disable-libssp --with-dwarf2 --program-prefix=avr-
Thread model: single
gcc version 9.3.0 (GCC)


 

Any help will be appreciated!

 

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

Checkout __memx (effectively 23 bit pointers)

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

clawson wrote:
Checkout __memx (effectively 23 bit pointers)

 

From a Google search... that leads to HERE:

 

clawson wrote:
It's a question of whether the pointer is const or the thing it is pointing to is const. . Bit why _memx anyway? Are you trying to make a routine that is agnostic about whether the data is in flash or RAM? Otherwise why not simply __flash?

 

Call me still confused. sad

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

The __flash keyword is a qualifier like const and volatilve.

Use __flash with the same syntax.

IIRC if one has more than 64K bytes of flash,

one might need any of __flash0, __flash1, __flash2 and __flash3.

I think one can also use __memx,

but not for defining in-flash data.

Iluvatar is the better part of Valar.

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

skeeve wrote:

The __flash keyword is a qualifier like const and volatilve.

Use __flash with the same syntax.

IIRC if one has more than 64K bytes of flash,

one might need any of __flash0, __flash1, __flash2 and __flash3.

I think one can also use __memx,

but not for defining in-flash data.

 

Well, it seems that neither "__flash" nor "__memx" are supported by my toolchain. I tried the uppercase versions which are meant to check for the functions (i.e. #ifdef __MEMX) and neither of them exist (flash or memx).

 

I cut-n-pasted the examples in the "AVR Named Spaces" docs and no-go.​

 

My toolchain is up to date (avr-gcc 9.3.0 and avr-libc 2.0.0) so that shouldn't be the problem.

 

If it matters, I'm trying to do this on a MEGA2560.

 

At this point I have no clue what to do. 

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

C or c++? Did these ever make it over to c++?

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

 

The postings on this site are my own and do not represent Microchip’s positions, strategies, or opinions.

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

If you can get the pgm_read_xx to work, stick with that.

 

The __flash qualifier is lpm only, the __flashN versions will get you an elpm with the rampz value set to N (RAMPZ=N, whether correct or not). There is nothing smart underneath (it seems) and without any other work the const data is still in the .progmem section and does not mean it gets moved to the correct 64k segment. I think what you would have to do to get those to work is both get the constant data into specific addresses, then use the appropriate __flashN qualifier., along with the fact that you cannot cross any 64k boundary. 

 

Create a simple test to figure things out. The following shows that the linker places the constant data in .progmem, but does not move them to the appropriate 64k segments (so __flashN is an access only qualifier it seems, and has no knowledge which 64 segment the data is actually located).  I have not used >64k avr, so have no experience, but I think using the pgm_read stuff will probably be easier as you also no longer have to worry about 64k boundaries. 

 

Once you get past 64k, then 32bit mcu's start to look better because you no longer have to deal with these kind of things. You may get a new set of problems, but it won't be because of addressing limitations/workarounds.

 

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

//volatile to get code emitted

__flash  volatile const char font1[2] = {0}; //lpm only
__flash2 volatile const char font2[2] = {0}; //elpm w/rampz=2
__flash3 volatile const char font3[2] = {0}; //elpm w/rampz=3

 

int main(void) {
    (void)font1[0];
    (void)font2[0];
    (void)font3[0];
    pgm_read_byte_far(pgm_get_far_address(font2));
    for(;;){};
}

/*
 00000106 <main>:
 (void)font1[0]; //lpm access (__flash)
 106:    e8 ee           ldi    r30, 0xE8    ; 232
 108:    f0 e0           ldi    r31, 0x00    ; 0
 10a:    84 91           lpm    r24, Z
 

 (void)font2[0]; //elpm access (__flash2), RAMPZ=2, except its not in page2
 10c:    e6 ee           ldi    r30, 0xE6    ; 230
 10e:    f0 e0           ldi    r31, 0x00    ; 0
 110:    02 e0           ldi    r16, 0x02    ; 2
 112:    0b bf           out    0x3b, r16    ; 59
 114:    86 91           elpm    r24, Z
 
 
(void)font3[0]; //elpm access (__flash3), RAMPZ=3, except its not in page3
 116:    e4 ee           ldi    r30, 0xE4    ; 228
 118:    f0 e0           ldi    r31, 0x00    ; 0
 11a:    03 e0           ldi    r16, 0x03    ; 3
 11c:    0b bf           out    0x3b, r16    ; 59
 11e:    86 91           elpm    r24, Z
 

 pgm_read_byte_far(pgm_get_far_address(font2)); //elpm w/correct 24bit address
 120:    86 ee           ldi    r24, 0xE6    ; 230
 122:    90 e0           ldi    r25, 0x00    ; 0
 124:    a0 e0           ldi    r26, 0x00    ; 0
 126:    bb 27           eor    r27, r27
 128:    ab bf           out    0x3b, r26    ; 59
 12a:    fc 01           movw    r30, r24
 12c:    87 91           elpm    r24, Z+

 

 
 *(.progmem*)
 .progmem3.data
                0x00000000000000e4        0x2 build/default/production/newmain.o
                0x00000000000000e4                font3
 .progmem2.data
                0x00000000000000e6        0x2 build/default/production/newmain.o
                0x00000000000000e6                font2
 .progmem.data  0x00000000000000e8        0x2 build/default/production/newmain.o
                0x00000000000000e8                font1

*/

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

meolsen wrote:
C or c++? Did these ever make it over to c++?

Nope the C++ guys will not allow their GCC compiler to be "tainted" by special support for some micro almost no one has ever heard of. So if it's C++ there's no option, it has to be PROGMEM, pgm_read*() etc.
.
Of course one can make a project with a mix of C and C++

Last Edited: Sun. Apr 26, 2020 - 02:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

If you can get the pgm_read_xx to work, stick with that.

The __flash qualifier is lpm only

True; however when I switched out all my PROGMEM & pgm_read_xxx for __flash in what was a 28K image, I got approximately 10% code size reduction. I'm sure the compiler was better able to optimise stuff around the LPM, whereas I guess the more opaque pgm_read_xxx __asm__ macros pretty much prevented any optimisation around them.

 

I therefore recommend using __flash even if it's a big refactor. Even if you don't get as big a code size reduction as I did; the code reads so very much cleaner.

 

Now I'm on PIC24 & PIC32; I don't have to worry about all that PROGMEM rubbish, and boy am I glad, I'd vote for it being the worst feature of AVR.

 

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

curtvm wrote:

If you can get the pgm_read_xx to work, stick with that.

 

Yes, "pgm_read_xxx" does work.

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

Just to be clear then. Is your lack of __flash because you are using C++ or because you are using an avr-gcc from before 4.6 ?

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

clawson wrote:

Just to be clear then. Is your lack of __flash because you are using C++ or because you are using an avr-gcc from before 4.6 ?

 

I tried the "#ifdef __FLASH" in both C and CPP, neither worked. I also tried the examples shown in the "Named address spaces" page  LINK  and those didn't work either.

 

My avr-gcc is V9.3.0 and avr-libc V2.2.0 and the source was compiled with a simple Makefile.

 

I tried it on a 328p, a 2560 and a 1284p. Same result each time.

 

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

Krupski wrote:
didn't work

details ?

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

Krupski wrote:
didn't work

details ?

 

 

Currently typing on a Kindle Fire! LOL. If I recall, the error was "Underined reference to __flash". I'll check tomorrow and post the correct data.

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

You're must post the code you wrote, what you're implying is that __FLASH is defined, but the __flash extension fails. This makes no sense.

 

Similar to the gnu.org example, I wrote:

#ifdef __FLASH
const __flash char VersionTxt[] = "Version 1.0.1";
#endif /* __FLASH */

No surprises - it works.

 

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

One can quickly check the compiler with:

C:\>avr-gcc -E -dM - < NUL | grep FLASH
#define __FLASH 1
#define __BUILTIN_AVR_FLASH_SEGMENT 1

C:\>

so my one has __flash support. (this is actually launching a command prompt from AS7 so it uses the AS7 version of avr-gcc)

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

clawson wrote:

One can quickly check the compiler with:

C:\>avr-gcc -E -dM - < NUL | grep FLASH
#define __FLASH 1
#define __BUILTIN_AVR_FLASH_SEGMENT 1

C:\>

so my one has __flash support. (this is actually launching a command prompt from AS7 so it uses the AS7 version of avr-gcc)

 

Just did it (modified the command line slightly for Linux... 'cause I don't do Windoze):

root@michael:[1][/]# avr-gcc -E -dM - < /dev/null | grep -i 'FLASH'
#define __FLASH 1
#define __FLASH1 1
#define __FLASH2 1
#define __FLASH3 1
#define __FLASH4 1
#define __FLASH5 1
#define __BUILTIN_AVR_FLASH_SEGMENT 1

 

Then tried this:

root@michael:[1][/]# avr-gcc -E -dM - < /dev/null | grep -i 'MEMX'
#define __MEMX 1

 

Very interesting.

 

Even more interesting... NOW it compiles! What the heck?

 

main.c:

#include <stdio.h>
#include "main.h"

int i = 5;

int main (void)
{
    /* Return 17 by reading from flash memory */
    return array[array[i]];
}

main.h:

#ifndef MAIN_H
#define MAIN_H

/* Locate array[] in flash memory */
const __flash uint8_t array[] = { 3, 5, 7, 11, 13, 17, 19 }; // these both work
//const __memx uint8_t array[] = { 3, 5, 7, 11, 13, 17, 19 }; // these both work

#endif

Build command line:

/opt/local/bin/avr-gcc -mmcu=atmega328p -DF_CPU=16000000UL -Wall -O2 -fdiagnostics-color=auto -pipe -g -lm main.c -o main

Resulting code:

///// snip interrupt vectors & stuff /////

00000068 <array>:
  68:   03 05 07 0b 0d 11 13 00                             ........

0000009e <main>:
int i = 5;

int main (void)
{
    /* Return 17 by reading from flash memory */
    return array[array[i]];
  9e:   e0 91 00 01     lds r30, 0x0100 ; 0x800100 <i>
  a2:   f0 91 01 01     lds r31, 0x0101 ; 0x800101 <i+0x1>
  a6:   e8 59           subi    r30, 0x98   ; 152
  a8:   ff 4f           sbci    r31, 0xFF   ; 255
  aa:   e4 91           lpm r30, Z
  ac:   f0 e0           ldi r31, 0x00   ; 0
  ae:   e8 59           subi    r30, 0x98   ; 152
  b0:   ff 4f           sbci    r31, 0xFF   ; 255
  b2:   84 91           lpm r24, Z
}
  b4:   90 e0           ldi r25, 0x00   ; 0
  b6:   08 95           ret

000000b8 <_exit>:
    cli
  b8:   f8 94           cli

000000ba <__stop_program>:
    rjmp    __stop_program
  ba:   ff cf           rjmp    .-2         ; 0xba <__stop_program>

 

 

I put the flash code into the .h file rather than in the .c file to be sure that didn't make a difference (because that's how I tried it before when it didn't work). And, the font data that I have in flash is defined in "fontname.h" files, so it has to work in an .h file (which it does).

 

I must have made some dumb blunder last time I tried it. I had been up for 2 days straight and I guess my brain turned to mush.

 

Oh well, it's working now!  laugh

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

Krupski wrote:
And, the font data that I have in flash is defined in "fontname.h" files

Rookie error right there. They should be "fontname.c" files because they declare define variables.

 

But because you're "touching" those files you can fix it now; you can still #include a ".c" file if you don't want to change the Makefile.

 

<edit>

WTF: I just noticed your test code:

    /* Return 17 by reading from flash memory */
    return array[array[i]];

No No No - that's really:

    /* Return 17 by reading from flash memory */
    // return array[array[i]];
    // return array[array[5]];
    return array[17];

Which not what the comment says.

 

</edit>

 

Last Edited: Thu. Apr 30, 2020 - 09:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
you can still #include a ".c" file

Doesn't that just bring you back to the same place - all you've done then is just renamed .h to .c  ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
    /* Return 17 by reading from flash memory */
    return array[array[i]];

Presumably the intention was simply:


    /* Return 17 by reading from flash memory */
    return array[i];

 

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

N.Winterbottom wrote:

Krupski wrote:
And, the font data that I have in flash is defined in "fontname.h" files

Rookie error right there. They should be "fontname.c" files because they declare define variables.

 

But because you're "touching" those files you can fix it now; you can still #include a ".c" file if you don't want to change the Makefile.

 

<edit>

WTF: I just noticed your test code:

    /* Return 17 by reading from flash memory */
    return array[array[i]];

No No No - that's really:

    /* Return 17 by reading from flash memory */
    // return array[array[i]];
    // return array[array[5]];
    return array[17];

Which not what the comment says.

 

</edit>

 

 

This was cut-n-pasted from the web page that had the example. *I* didn't write the code! (aside from splitting it into .c and .h files). blush

 

Anyway, my "goal" was just to get it to compile, because previously it would not, claiming that __flash was not defined (i.e. didn't even exist in the toolchain). This was simply due to me being tired and making a mistake. It really IS there.

 

As far as putting the flash data in .h files, why is that wrong? These .h files are bitmap fonts used by a library that runs a 128x64 px VFD display. It's setup like this:

 

allfonts.h { #include font1.h, font2.h, ... fontn.h }

driver.h { #include allfonts.h, driver's vars & function prototypes }

driver.cpp { #include driver.h, graphics functions code }

 

Of course, only the fonts actually used end up consuming flash. If the program that uses (#includes) the VFD driver says "VFD.setFont (ibm_8x16) ;" then only THAT font ends up in the AVR binary.

 

Make sense? I can show it in better detail if it will help.

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

clawson wrote:

    /* Return 17 by reading from flash memory */
    return array[array[i]];

Presumably the intention was simply:


    /* Return 17 by reading from flash memory */
    return array[i];

 

 

I assume you are correct. I am going to actually try a small test program to see if it actually WORKS instead of merely compiling without error.

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

Oh yeah I see what you mean. It's the manual that is wrong:

 

https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html

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

OK, here is some really strange problem. I made a test program to define uint8_t data bytes in an .h file, then access them using array[ index ]. Worked like a charm. I defined the test data like this:

 

static const __memx uint8_t array[] = {
    0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, // 30
    0x00, 0x42, 0x7F, 0x40, 0x00, 0x00,
    0x72, 0x49, 0x49, 0x49, 0x46, 0x00,
    0x22, 0x41, 0x41, 0x49, 0x36, 0x00,
    0x18, 0x14, 0x12, 0x7F, 0x10, 0x00,
    0x27, 0x45, 0x45, 0x45, 0x39, 0x00,
    0x3E, 0x49, 0x49, 0x49, 0x30, 0x00,
    0x61, 0x11, 0x09, 0x05, 0x03, 0x00,
    0x36, 0x49, 0x49, 0x49, 0x36, 0x00,
    0x06, 0x49, 0x49, 0x49, 0x36, 0x00,
    0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
    0x00, 0x80, 0x64, 0x00, 0x00, 0x00,
    0x08, 0x14, 0x22, 0x41, 0x00, 0x00,
    0x14, 0x14, 0x14, 0x14, 0x14, 0x00,
    0x00, 0x41, 0x22, 0x14, 0x08, 0x00,
    0x02, 0x01, 0x51, 0x09, 0x06, 0x00,
};

 

Result of running the code (copied from the serial terminal):

 

array at address 0x17D0 contains

0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x00, 0x42,
0x7F, 0x40, 0x00, 0x00, 0x72, 0x49, 0x49, 0x49,
0x46, 0x00, 0x22, 0x41, 0x41, 0x49, 0x36, 0x00,
///// snip /////
0x08, 0x14, 0x22, 0x41, 0x00, 0x00, 0x14, 0x14,
0x14, 0x14, 0x14, 0x00, 0x00, 0x41, 0x22, 0x14,
0x08, 0x00, 0x02, 0x01, 0x51, 0x09, 0x06, 0x00,

 

And indeed, the data is in flash:

 

000017d0 <array>:
    17d0:   3e 51 49 45 3e 00 00 42 7f 40 00 00 72 49 49 49     >QIE>..B.@..rIII
    17e0:   46 00 22 41 41 49 36 00 18 14 12 7f 10 00 27 45     F."AAI6.......'E
    17f0:   45 45 39 00 3e 49 49 49 30 00 61 11 09 05 03 00     EE9.>III0.a.....
    1800:   36 49 49 49 36 00 06 49 49 49 36 00 00 00 24 00     6III6..III6...$.
    1810:   00 00 00 80 64 00 00 00 08 14 22 41 00 00 14 14     ....d....."A....
    1820:   14 14 14 00 00 41 22 14 08 00 02 01 51 09 06 00     .....A".....Q...
    1830:   de 01 e3 01 3d 01 3d 01 d9 01 d3 01 cd 01 c7 01     ....=.=.........
    1840:   3d 01 3d 01 3d 01 3d 01 3d 01 3d 01 3d 01 3d 01     =.=.=.=.=.=.=.=.
    1850:   3d 01 3d 01 3d 01 3d 01 c1 01 3d 01 3d 01 3d 01     =.=.=.=...=.=.=.
    1860:   3d 01 3d 01 3d 01 3d 01 3d 01 bc 01 b6 01           =.=.=.=.=.....

 

Now, to test what I really need it for... a font file. Note that static const uint8_t ibm_8x16[] __attribute__((progmem)) = { .... }; works, has worked, always worked. So, I changed it as such:

 

#ifndef IBM_8X16_H
#define IBM_8X16_H

//static const uint8_t ibm_8x16[] __attribute__((progmem)) = {
static const __memx uint8_t ibm_8x16[] = {

    0x08, // width (base + 0)
    0x10, // height (base + 1)
    0x00, // horizontal gap (base + 2)
    0x00, // vertical gap (base + 3)
    0x20, // first char (base + 4)
    0x7F, // last char (base + 5)
    0x10, // bytes per char (base + 6)

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20
    0x00, 0x00, 0x38, 0xFC, 0xFC, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0D, 0x00, 0x00, 0x00,
    0x00, 0x0E, 0x1E, 0x00, 0x00, 0x1E, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x20, 0xF8, 0xF8, 0x20, 0xF8, 0xF8, 0x20, 0x00, 0x02, 0x0F, 0x0F, 0x02, 0x0F, 0x0F, 0x02, 0x00,
    0x38, 0x7C, 0x44, 0x47, 0x47, 0xCC, 0x98, 0x00, 0x06, 0x0C, 0x08, 0x38, 0x38, 0x0F, 0x07, 0x00,
    0x30, 0x30, 0x00, 0x80, 0xC0, 0x60, 0x30, 0x00, 0x0C, 0x06, 0x03, 0x01, 0x00, 0x0C, 0x0C, 0x00,
    ////// the rest snipped out //////

 

Tried to compile a test program that displays rotary encoder data on the VFD screen using Arduino. Note that the program compiles and works PERFECTLY compiled in Arduino and using PROGMEM for the font data. But, doing NOTHING other than replacing PROGMEM with _memx, I get this result:

 

In file included from /usr/share/arduino-1.0.6/libraries/Noritake_GUU100/fonts/allFonts.h:48,
                 from /usr/share/arduino-1.0.6/libraries/Noritake_GUU100/Noritake_GUU100.h:35,
                 from test.ino:1:
/usr/share/arduino-1.0.6/libraries/Noritake_GUU100/fonts/ibm_8x16.h:27:14: error: '__memx' does not name a type
   27 | static const __memx uint8_t ibm_8x16[] = {
      |              ^~~~~~
test.ino: In function 'int main()':
test.ino:229:12: error: 'ibm_8x16' was not declared in this scope; did you mean 'ibm_8x14'?

 

Note also that my Arduino "IDE" also runs avr-gcc 9.3.0 - the same compiler for everything whether I use source and a Makefile, or an ".ino" file and compile it with Arduino. The only reason I even use the Arduino "IDE" is as a "compile" button. Also, it's easier to include my libraries into the code rather then building custom Makefiles to do it.

 

Why on earth can't avr-gcc find the __flash or __memx stuff when compiling with Arduino, but it's perfectly fine without it?

 

This is driving me nuts

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

clawson wrote:

Oh yeah I see what you mean. It's the manual that is wrong:

 

https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html

 

Yeah, and since all I cared about was "will it compile" I never even noticed it.

 

Actually, I think the example would work in that the array is indexed by an element of the array... as long as array element contained an "index" within the size of the array..... would work I think.

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

Krupski wrote:
Why on earth can't avr-gcc find the __flash or __memx stuff when compiling with Arduino

Doesn't Arduino run source files through the C++ compiler ? In which case, #8 covers that.

 

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

__memx creates a "smart POINTER" to a 23 bit addressed RAM/Flash object.

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

N.Winterbottom wrote:
But because you're "touching" those files you can fix it now; you can still #include a ".c" file if you don't want to change the Makefile.
Why?

Putting a global definition in a header file is asking for trouble regardless of its suffix.

Move the definition into an existing .c file and put the declaration in the header.h file.

Iluvatar is the better part of Valar.

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

>Why on earth can't avr-gcc find the __flash or __memx stuff when compiling with Arduino

 

Arduino-

https://godbolt.org/z/EaVchs

 

It uses C++. That online compiler will also use C++ even when you switch to the 'regular' avr-gcc c compiler (it uses Arduino compiler for these as well it seems, so cannot check out differences in C/C++ because you always get the C++ compiler).

 

You also cannot get sneaky and set an attribute of section .progmemx.data, because that has no special meaning in the C++ version. Which makes sense, as has already been discussed.

 

So its possible, you may end up back here-

 

>If you can get the pgm_read_xx to work, stick with that.

 

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

clawson wrote:
__memx creates a "smart POINTER" to a 23 bit addressed RAM/Flash object.

 

I don't know what that means. What is a "smart pointer"?

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

skeeve wrote:

N.Winterbottom wrote:
But because you're "touching" those files you can fix it now; you can still #include a ".c" file if you don't want to change the Makefile.
Why?

Putting a global definition in a header file is asking for trouble regardless of its suffix.

Move the definition into an existing .c file and put the declaration in the header.h file.

 

I have many different font files (over 30). I can't (don't want to) put them all into the driver source!

 

I see no difference between putting the data into an .h file or putting it into a .c file and then putting the font declarations into .h files (then of course including all the .h files in "allFonts.h" which in turn is included in the driver's .h file).

 

Or do you mean that "__flash" or "__memx" will work now because they would be in .c files instead of .h files? 

 

Sorry if these are stupid questions, but I'm having a tough time understanding this stuff. Especially why I shouldn't put code in an .h file. Everyone does it!

 

Telling me "I'm asking for trouble" using an .h file like that explains nothing. Please tell me WHY it's wrong, what trouble it will cause. 

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

Krupski wrote:

 

Why on earth can't avr-gcc find the __flash or __memx stuff when compiling with Arduino, but it's perfectly fine without it?

 

 

It's not Arduino.  Try your test program renamed as .cpp instead of .c and you'll get the same error.  __flash is not a valid identifier in c++.

I've had no problem using __flash with Arduino libraries.  Include your .h with the __flash arrays from a .c file, declare the function extern "C" in your .ino(or .cpp) file, and you'll be fine.

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

It is easy enough to write functions that expect data in far memory.
It is simple to specify a named section for the far data.
And tell the linker to place the named section in far memory i.e. after the executable .text section.
i.e. all C++ files or a mix of C, C++, S files.
.
You should be able to put your _memx code in the C file and still have external linkage to your regular C++ project.
.
Your multiple fonts is similar to my multiple JPEG images embedded in far memory.
.
Life is much better with flat memory like ARM or ESP.
.
David.

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

"Dare to be naïve." - Buckminster Fuller

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

Krupski wrote:
Telling me "I'm asking for trouble" using an .h file like that explains nothing. Please tell me WHY it's wrong, what trouble it will cause.
It's invalid to define the same object in two different translation units.

This is fundamental to both C and C++.

Often the linker will complain and quit.

Sometimes the linker will complain and continue.

Sometimes the linker will silently continue.

One possible result is that you have one complete copy

of all your fonts for each time the header file is included.

Iluvatar is the better part of Valar.

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

skeeve wrote:

Krupski wrote:
Telling me "I'm asking for trouble" using an .h file like that explains nothing. Please tell me WHY it's wrong, what trouble it will cause.

It's invalid to define the same object in two different translation units.

This is fundamental to both C and C++.

 

It depends on what you mean by object.  You can safely define types, such as an enum in two different translation units.

 

I agree that in general you should avoid defining functions, arrays, etc in header files.  For the advanced programmer doing things like header-only libraries, there are ways of doing it.

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

ralphd wrote:

skeeve wrote:

Krupski wrote:
Telling me "I'm asking for trouble" using an .h file like that explains nothing. Please tell me WHY it's wrong, what trouble it will cause.

It's invalid to define the same object in two different translation units.

This is fundamental to both C and C++.

It depends on what you mean by object.  You can safely define types, such as an enum in two different translation units.
I was referring to C's idea of object.

I avoided the term variable because fonts are usually const and I wanted to avoid the notion of a constant variable.

Types and enums are definitely not objects.

Quote:
I agree that in general you should avoid defining functions, arrays, etc in header files.  For the advanced programmer doing things like header-only libraries, there are ways of doing it.
The functions need to be static inline.

The variables and other objects, including arrays of them, need to be static.

The rules 'tain't that difficult.

Iluvatar is the better part of Valar.

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

Krupski wrote:
Telling me "I'm asking for trouble" using an .h file like that explains nothing. Please tell me WHY it's wrong, what trouble it will cause.

OK I started that so here's a real example:

 

I've previously had to work on fairly large code-base where the original programmer's conception of a '.h' header file was simply an extension of it's associated '.c' file. The flawed thinking was/is: "main.h" is a header file for main.c"   So main.h was used to help de-clutter main.c and contained many of the less often edited definitions and was only ever included into "main.c".

 

In order for other C files in the project to see the functions and variables defined in main.c  I found; for example:

extern int8_t foo(int_8_t a, int8_t b);  in multiple files throughout the project.

You might see where this scheme fails because in a few other files I found:

extern int8_t foo(int_16_t a, int16_t b); This lead to subtle (just now & then) type bugs.

 

Instead you should consider "main.h" as a header file for any "other.c" file so that it can use those variables and functions you consider public that are defined in "main.c".

 

Of course, a well written "main.h" can also be included in "main.c" where the compiler will check that the declarations properly match the definitions avoiding the above scenario

 

Last Edited: Sun. May 3, 2020 - 10:37 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

ralphd wrote:

skeeve wrote:

It's invalid to define the same object in two different translation units.

This is fundamental to both C and C++.

It depends on what you mean by object.  You can safely define types, such as an enum in two different translation units.
I was referring to C's idea of object.

I avoided the term variable because fonts are usually const and I wanted to avoid the notion of a constant variable.

Types and enums are definitely not objects.

Quote:
I agree that in general you should avoid defining functions, arrays, etc in header files.  For the advanced programmer doing things like header-only libraries, there are ways of doing it.
The functions need to be static inline.

The variables and other objects, including arrays of them, need to be static.

The rules 'tain't that difficult.

The rules aren't as simple as you claim either.

Sometimes functions can't be inline, for example naked functions.  I've worked around that in the past by making the function weak.

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

ralphd wrote:
The rules aren't as simple as you claim either.

Sometimes functions can't be inline, for example naked functions.  I've worked around that in the past by making the function weak.

Now we are discussing extensions.

If said naked function cannot at least be static,

it ought not be in a header file and therefore ought not be in a .h file.

 

@Those who care: Very little of avr-gcc is extensions and even less is avr-specific extensions.

Iluvatar is the better part of Valar.

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

skeeve wrote:

ralphd wrote:

The rules aren't as simple as you claim either.

 

Sometimes functions can't be inline, for example naked functions.  I've worked around that in the past by making the function weak.

Now we are discussing extensions.

If said naked function cannot at least be static,

it ought not be in a header file and therefore ought not be in a .h file.

 

@Those who care: Very little of avr-gcc is extensions and even less is avr-specific extensions.

 

Do you have any solution for Arduino libraries that need configuration at build-time?

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

N.Winterbottom wrote:
Instead you should consider "main.h" as a header file for any "other.c" file so that it can use those variables and functions you consider public that are defined in "main.c".

 

Of course, a well written "main.h" can also be included in "main.c" where the compiler will check that the declarations properly match the definitions avoiding the above scenario

It should be, assuming one has a main.h .

That said, main.h should be pretty much empty.

elvis.h should be for the benefit of things that depend on elvis.c .

For the most part, main.c should depend on other things,

not other things on main.c .

 

Note that not every .h file need correspond to a .c file.

An example might be one defining pin selections.

Iluvatar is the better part of Valar.

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

ralphd wrote:
Do you have any solution for Arduino libraries that need configuration at build-time?
A solution to what?

 

If one wants, there are a couple ways to effectively replace Arduino header files without changing the Arduino source.

I had to do that once for licensing reasons.

In my case, I use a -I argument on avr-g++.

There is also a flag to implicitly include a header file at the beginning of a compilation unit.

Iluvatar is the better part of Valar.

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

You can also create your own include-guard-like access define. For anything other than the holder of the keyword, you have a normal include providing info a header is able to provide. For the single c source file which needs to create the font data, you get the data since you had the keyword.

 

There may be better solutions, but I think this is sound. You can keep all the various fonts in their own h files, and they only turn into data for the single source file that has the keyword. You are relying on the linker optimization to discard the unused fonts, but I doubt that is a problem and you will know if that happens (code size), and with the single c source file creating the data you know you will not be getting duplicates in any case.

 

//font1.h

//font info here
extern __memx const char font1[8000];
#ifdef __ONLY_FONTS_C_CAN_CREATE__
    //font data here
    __memx const char font1[8000] = {0};
#endif

 

//font2.h

//font info here
extern __memx const char font2[8000];
#ifdef __ONLY_FONTS_C_CAN_CREATE__
    //font data here
    __memx const char font2[8000] = {0};
#endif

 

//fonts.h
//can group them all so only need to include fonts.h
#include "font1.h"
#include "font2.h"

 

//fonts.c
//this is where the data turns into reality
//and optimizer 'should' discard unused fonts

//and only one copy of data will be emitted

//only this file has the keyword to create the data
#define __ONLY_FONTS_C_CAN_CREATE__
#include "fonts.h"

 

//main.c

#include <avr/io.h>
#include "fonts.h"
int main(void) {
    SREG = font1[SREG];

    SREG = font2[SREG];
    for(;;){}
}

 

>Sometimes functions can't be inline, for example naked functions.

 

In C++, I put the function inside a struct, and it gets emitted only once (this is in a header), if not in a struct it gets emitted for each translation unit that sees it. I suppose since the code is inside a struct (no separate declaration) it becomes automatically inline.

 

struct RSTCTRL_BOOT {
    [[ using gnu : section(".init5"), naked, used ]]
    static void RstctrlBoot(){
        //get/store flags, if no flags, software reset
        if( not Rstctrl::flags().RSTFR ) Rstctrl::softReset();
    }
};

 

I have header only 'drivers' for almost all avr0/1 peripherals (any avr0/1) in c++, and am using c++17 features like inline variables. I do have one cpp file for the interrupt code because there is some c code in there that may be difficult to get to work in a header (haven't tried, though).

Last Edited: Sun. May 3, 2020 - 05:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

ralphd wrote:
Do you have any solution for Arduino libraries that need configuration at build-time?
A solution to what?

 

If one wants, there are a couple ways to effectively replace Arduino header files without changing the Arduino source.

I had to do that once for licensing reasons.

In my case, I use a -I argument on avr-g++.

There is also a flag to implicitly include a header file at the beginning of a compilation unit.

 

How can an Arduino library specify build arguments?

For code built with make, build time configuration is easy and I'm familiar with various techniques from -DFOO, conditional includes, conditional linking, etc.

 

Just recently the Arduino library specification was expanded to allow specifying ldflags.  I don't think ading a compile-time include (-I), or define (-D) is possible.

https://arduino.github.io/arduin...

 

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

I've never built anything with the Arduino CLI or IDE.

If they present insurmountable obstacles,

the obvious thing is not to use them.

Iluvatar is the better part of Valar.

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

The only insurmountable obstacle is punters that choose to "never use" but "make pronouncements".