Description
A simple program (say, Blink) that includes more than 64k of pgmspace data (for MEGA, MEGA2560, MEGA ADK, various 1284 boards) will fail to work correctly.
One of the reasons is that Arduino.h includes macro definitions like:
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
Due to link order, the digital_pin_to_port_PGM[] array will be pushed AFTER the explicitly defined pgmspace variables, and it will no longer be readable by pgm_read_byte.
Using pgm_read_byte_far() seems like overkill.
It turns out that apparently gcc has a similar problem with pgmspace variables that IT uses, because the default linker map includes two entries for progmem data:
/* For data that needs to reside in the lower 64k of progmem. */
*(.progmem.gcc*)
*(.progmem*)
This means that MEGA and etc can be fixed by a relative simple patch to their pins_arduino.h file, putting the pin tables in section .progmem.gcc.arduinocore instead of the normal .progmem (as per the attached diff file)
Activity
WestfW commentedon Aug 4, 2014
(Um. Can I not attach source and diff files to an issue?)
matthijskooijman commentedon Aug 4, 2014
Probably not - I guess you could open a pullrequest, or include the diff in a comment (indent it by for spacs or put ``` before and after it to make it display as code).
As for the fix you suggest, I'm wondering why it works. AFAIU, any new sections introduced have to be manually listed in the linker script? Though perhaps due to
-fdata-sections
each variable already gets its own section and they are auto-joined? If so, I believe that this fix works because "arduinocore" sorts early?WestfW commentedon Aug 6, 2014
The linker script already has
*(progmem.gcc*)
in the appropriate place. The section names from -fdata-sections take the form of ".mainsection.variablename", so almost all of the linker script sections are wildcarded. (Hmm. I wonder why the wildcards didn't come out in my first post. I'm pretty sure they were in the COPY...)WestfW commentedon Aug 6, 2014
Grr. Removed them again. cursed markdown language!
(edited to fix?)
matthijskooijman commentedon Aug 6, 2014
It seems that github/markdown is interpreting your asterisks as meaning italic text. You can probably escape them with *, or quite with `.
Thanks for clarifying, I can see why your fix works. I'm wondering if "arduinocore" is the best approach though - it sorts low, but if a user declares a big "aa" PROGMEM variable, things still break. I'm not sure what the rules for section names are, but if they're the same as variable names (start with a latter, contain letters, numbers and underscores IIRC), something like
.progmem.gcc.A00_arduinocore
might be good?WestfW commentedon Aug 6, 2014
WestfW commentedon Aug 6, 2014
I'm pretty sure that the
progmem.gcc.*
will always sort before theprogmem.variablename
values created by normal user pgmspace statements. Otherwise it wouldn't work for gcc internals, either.matthijskooijman commentedon Aug 6, 2014
Oh, right. I was assuming that variables would get
progmem.gcc.variablename
, which is apparently wrong. I'm wondering if it's ok to 'invade' theprogmem.gcc
namespace like this, but I guess it will work and as long as we don't push > 64k of data in it, it should be ok :-)Not sure I like the "MYPROGMEM" name though. Wouldn't GCCPROGMEM be better? In either case, there should be a fat comment explaining why this is needed and why this works :-)
oqibidipo commentedon Feb 14, 2017
You must also modify these lines in Arduino.h to make it work
geez0x1 commentedon May 19, 2017
Today we spent most of the day debugging why our Arduino immediately crashed when increasing data use in PROGMEM, even when that data was not being used. Spent ages trying to find problems with 32-bit pointers, etc. What made matters worse is that at some point disabling optimisations (
-O0
instead of-Os
) made it partially work, which we tried because the documentation forpgm_get_far_address
suggests:http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga8f4a87d8740f570871d7e041750efed3
Turns out the problem was the one mentioned in this thread. Disabling optimisations just resulted in different code which somehow made something work at some point. Very confusing.
The patches by WestfW (needs to be done manually though) and oqibidipo appear to completely solve the problem.
As of this moment these patches are not implemented in the Arduino mainline code. My Debian packages may be slightly outdated(?), but the other machine is running the (most) recent packages with AVR 1.6.18 etc.
Is there any particular reason these patches have not been mainlined? Should we file a pull request?
Defined GCCPROGMEM for boards with ATmega 1280 and 2560 that have >64…
information-security commentedon May 27, 2017
Hi,
Any update on this? I tested the pull request. It somehow fixed the issue but some strange behaviours came up instead. I am not sure if it was caused by the change or it is from my code!
geez0x1 commentedon May 28, 2017
Can you be more specific as to what issues you found?
information-security commentedon May 29, 2017
I have two large arrays as well as a medium array:
extern imagedatatype gImage_img_header[];
extern imagedatatype gImage_img_sc1[];
extern imagedatatype gImage_img_sc2[];
As their names imply, these arrays contain image data. header is shown at top of TFT display, sc1 is shown below the header and sc2 is shown at bottom of the screen. When I reduce arrays' size (By reducing image quality) everything is shown as expected but when I switch to using large arrays considering the fact that I have applied the patch, sc1 and sc2 are fine but header is not shown and a cropped part of sc2 or sc1 (I can't recall which) is shown instead.
Note that without the patch sketch doesn't work at all.
Here is my sketch (excluded image data because they were too large):
geez0x1 commentedon May 29, 2017
In your code I don't see any mention of
PROGMEM
, i.e. using the PGM memory. I'm not sure what's causing the sketch to work once you apply the patch, but it appears to me you're simply filling up the RAM with the larger data, causing the sketch to crash. The IDE should show you a message how much of flash and RAM (global variables) you are using.information-security commentedon May 29, 2017
I don't think Mega2560 or any other arduino has that much ram available (31200 + 31200 + 14400). gImage_img_header, gImage_img_cs1 and gImage_img_cs2 are defined extern because they are placed into another .c file and I mentioned that I am excluding them because they are just large arrays. To find out how they are defined have a look below:
img_header.c
geez0x1 commentedon May 29, 2017
Ah, fair point. Can I ask which board you are using? Maybe I missed one (intended to patch only the files for boards that have >64K PGM available).
Another possibility is that there's other things that need to be patched as well, since more things are using PROGMEM:
I'm afraid I'm not knowledgeable enough on this, though.
information-security commentedon May 29, 2017
I am using Mega2560 R3.
geez0x1 commentedon May 29, 2017
The patch I submitted has patches for mega:
./hardware/arduino/avr/variants/mega/
. So in that case I'm afraid I don't know what's causing your issues. Maybe @WestfW has ideas.themobydisk commentedon Nov 30, 2018
As an alternative, is there a way I can label my arrays so that they will go after the PROGMEM arrays? Like
__attribute__((section(".progmem.gcc.zzzzzzz")))
? If I wasn't using Arduino Studio, couldn't I could put something on the linker command-line to state that my zzzzzzz section must start at 0x10000?FYI: Apparently the Ethernet library suffers from this problem too.
WestfW commentedon Nov 30, 2020
I don't think that "polluting" the gcc space is a significant problem, as long as the amount of PROGMEM used by Arduino is small, and I don't even know offhand what parts of gcc or avr-libc use PROGMEM (but they're also obviously small.)
Since Arduino has "matured" to the point of having custom linker scripts (eg for SAMD), a "correct" solution might involve using a linker script that defines arduino's own section of flash, in between the progmem.gcc and the progmem* segments.