How to abort build if flash/eeprom overflows

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

I am programming ATMega1284p with Atmel Studio 7 on Windows 10.

 

Several times now I have had a long debugging session only to discover that the program flash section has either overflowed the available flash or into the bootloader area or the eeprom overflowed.

 

It took a long time to find because it never occured to me to check the post-build step output showing the flash and eeprom sizes.

 

I am aware of "avr-size" but, as far as I can tell, it only displays the information.  I have so far not found a utility that will check the sizes against a specified parameter that, for example, causes an error if the flash size is over X bytes.  Specifically NOT the device flash size since space needs to be reserved for different size bootloaders.

 

Is there such a utility or method in Atmel Studio or WinAVR to detect this issue?

 

Thanks in advance ...

 

Chuck Hackett

 

This topic has a solution.
Last Edited: Thu. Feb 7, 2019 - 12:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Copy the generic .x file then edit the section sizes to the exact value for that chip then use with -T passed to the linker (-Wl,)

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

Not familiar with the ".x" files.  I checked the GCC docs and I see the -T option ...

 

Taking a look at the folder "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\lib\ldscripts" I see a lot of .x files.  How do I know which one to copy and modify?

 

Looking at some of them I see things like:

__TEXT_REGION_LENGTH__ = DEFINED(__TEXT_REGION_LENGTH__) ? __TEXT_REGION_LENGTH__ : 1024K;

 

Can I just define "__TEXT_REGION_LENGTH__" to the linker?

 

Thanks for the help ...

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

The easiest way (I find) to work out which of the .x files in ldscripts is used for a particular chip is to build a project with a .map file. For example I see:

Archive member included to satisfy reference by file (symbol)

c:/program files (x86)/atmel/studio/7.0/toolchain/avr8/avr8-gnu-toolchain/bin/../lib/gcc/avr/5.4.0/avr51\libgcc.a(_exit.o)
                              C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATmega_DFP/1.2.209/gcc/dev/atmega1284p/avr51/crtatmega1284p.o (exit)

Now the .map file (while it shows the consequences of the .x file used does not actually say which one it was) but look at the above.

 

Buried in there you can see "avr51" to this tells us that 1284p is one of the members of the avr51 family. By implication that means it is is avr51.x that is used by default. That file contains:

OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr")
OUTPUT_ARCH(avr:51)
__TEXT_REGION_LENGTH__ = DEFINED(__TEXT_REGION_LENGTH__) ? __TEXT_REGION_LENGTH__ : 128K;
__DATA_REGION_LENGTH__ = DEFINED(__DATA_REGION_LENGTH__) ? __DATA_REGION_LENGTH__ : 0xff00;
__EEPROM_REGION_LENGTH__ = DEFINED(__EEPROM_REGION_LENGTH__) ? __EEPROM_REGION_LENGTH__ : 64K;
__FUSE_REGION_LENGTH__ = DEFINED(__FUSE_REGION_LENGTH__) ? __FUSE_REGION_LENGTH__ : 1K;
__LOCK_REGION_LENGTH__ = DEFINED(__LOCK_REGION_LENGTH__) ? __LOCK_REGION_LENGTH__ : 1K;
__SIGNATURE_REGION_LENGTH__ = DEFINED(__SIGNATURE_REGION_LENGTH__) ? __SIGNATURE_REGION_LENGTH__ : 1K;
__USER_SIGNATURE_REGION_LENGTH__ = DEFINED(__USER_SIGNATURE_REGION_LENGTH__) ? __USER_SIGNATURE_REGION_LENGTH__ : 1K;
MEMORY
{
  text   (rx)   : ORIGIN = 0, LENGTH = __TEXT_REGION_LENGTH__
  data   (rw!x) : ORIGIN = 0x800100, LENGTH = __DATA_REGION_LENGTH__
  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = __EEPROM_REGION_LENGTH__
  fuse      (rw!x) : ORIGIN = 0x820000, LENGTH = __FUSE_REGION_LENGTH__
  lock      (rw!x) : ORIGIN = 0x830000, LENGTH = __LOCK_REGION_LENGTH__
  signature (rw!x) : ORIGIN = 0x840000, LENGTH = __SIGNATURE_REGION_LENGTH__
  user_signatures (rw!x) : ORIGIN = 0x850000, LENGTH = __USER_SIGNATURE_REGION_LENGTH__
}

So edit the limits here.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didn't like modifying the .x files since when I move to a different processor I would have to remember research and modify a different .x file. 

 

Doing some research I came across this:

 

https://stackoverflow.com/questions/10032598/gnu-ld-how-to-override-a-symbol-value-an-address-defined-by-the-linker-script

 

I modified my "Other Linker Flags" in the project properties to:

 

-Wl,--cref,--defsym=__TEXT_REGION_LENGTH__=512

 

(The 512 is obviously wrong but I wanted to force the build to fail as a test)

 

This caused the link to fail with the following:

 

BC002.elf section `.text' will not fit in region `text'

region `text' overflowed by 123102 bytes
 

Success!

 

Thanks for the suggestion to look at the .x files.  Without that I would not have discovered this.

 

Regards,

 

Chuck Hackett

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

Well thank you in fact. While the current X files have:

__TEXT_REGION_LENGTH__ = DEFINED(__TEXT_REGION_LENGTH__) ? __TEXT_REGION_LENGTH__ : 128K;
__DATA_REGION_LENGTH__ = DEFINED(__DATA_REGION_LENGTH__) ? __DATA_REGION_LENGTH__ : 0xff00;
__EEPROM_REGION_LENGTH__ = DEFINED(__EEPROM_REGION_LENGTH__) ? __EEPROM_REGION_LENGTH__ : 64K;
__FUSE_REGION_LENGTH__ = DEFINED(__FUSE_REGION_LENGTH__) ? __FUSE_REGION_LENGTH__ : 1K;
__LOCK_REGION_LENGTH__ = DEFINED(__LOCK_REGION_LENGTH__) ? __LOCK_REGION_LENGTH__ : 1K;
__SIGNATURE_REGION_LENGTH__ = DEFINED(__SIGNATURE_REGION_LENGTH__) ? __SIGNATURE_REGION_LENGTH__ : 1K;
__USER_SIGNATURE_REGION_LENGTH__ = DEFINED(__USER_SIGNATURE_REGION_LENGTH__) ? __USER_SIGNATURE_REGION_LENGTH__ : 1K;
MEMORY

it's never been clear to me how/where these symbols might be defined so your defsym= thing is very interesting.

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

First: I'm easily outleagued here, I never paid mych attention to these parts.

 

But it does look a bit strange to me. If errors like this are not caught my first though would be some error in the sizes of those sectrions in the linker script, or wherever they are defined.

I did bump into size limits a long time ago on a project, and from what I can remember the compiler / linker just cropped out with an error.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

The point here is that there's now well over 300 models of AVR. Already the avr-gcc compiler has to have 300+ copies of the CRunTime so the right one can be linked when building for a particular AVR but an analysis of the AVRs shows that there's only really something like 17 different architecture/families. For example Tiny don't have MUL, Only real big ones have EIND and so on. So there are 17 different copies of libm.a and 17 different libc.a and so on. So when a mega asked to do 17*36 then it can use MUL but a tiny uses a software simulation of MUL and so on.

 

To minimize the work in other areas it's the case that there are only 17 different .x files - the manual talks about these different architectures here:

 

https://www.nongnu.org/avr-libc/...

 

If you take just "avr5" architecture it contains all these (actually very similar) models of AVR:

avr5	at90can32	__AVR_AT90CAN32__
avr5	at90can64	__AVR_AT90CAN64__
avr5	at90pwm161	__AVR_AT90PWM161__
avr5	at90pwm216	__AVR_AT90PWM216__
avr5	at90pwm316	__AVR_AT90PWM316__
avr5	at90scr100	__AVR_AT90SCR100__
avr5	at90usb646	__AVR_AT90USB646__
avr5	at90usb647	__AVR_AT90USB647__
avr5	at94k	__AVR_AT94K__
avr5	atmega16	__AVR_ATmega16__
avr5	ata5790	__AVR_ATA5790__
avr5	ata5795	__AVR_ATA5795__
avr5	atmega161	__AVR_ATmega161__
avr5	atmega162	__AVR_ATmega162__
avr5	atmega163	__AVR_ATmega163__
avr5	atmega164a	__AVR_ATmega164A__
avr5	atmega164p	__AVR_ATmega164P__
avr5	atmega164pa	__AVR_ATmega164PA__
avr5	atmega165	__AVR_ATmega165__
avr5	atmega165a	__AVR_ATmega165A__
avr5	atmega165p	__AVR_ATmega165P__
avr5	atmega165pa	__AVR_ATmega165PA__
avr5	atmega168	__AVR_ATmega168__
avr5	atmega168a	__AVR_ATmega168A__
avr5	atmega168p	__AVR_ATmega168P__
avr5	atmega168pa	__AVR_ATmega168PA__
avr5	atmega169	__AVR_ATmega169__
avr5	atmega169a	__AVR_ATmega169A__
avr5	atmega169p	__AVR_ATmega169P__
avr5	atmega169pa	__AVR_ATmega169PA__
avr5	atmega16a	__AVR_ATmega16A__
avr5	atmega16hva	__AVR_ATmega16HVA__
avr5	atmega16hva2	__AVR_ATmega16HVA2__
avr5	atmega16hvb	__AVR_ATmega16HVB__
avr5	atmega16hvbrevb	__AVR_ATmega16HVBREVB__
avr5	atmega16m1	__AVR_ATmega16M1__
avr5	atmega16u4	__AVR_ATmega16U4__
avr5	atmega32	__AVR_ATmega32__
avr5	atmega32a	__AVR_ATmega32A__
avr5	atmega323	__AVR_ATmega323__
avr5	atmega324a	__AVR_ATmega324A__
avr5	atmega324p	__AVR_ATmega324P__
avr5	atmega324pa	__AVR_ATmega324PA__
avr5	atmega325	__AVR_ATmega325__
avr5	atmega325a	__AVR_ATmega325A__
avr5	atmega325p	__AVR_ATmega325P__
avr5	atmega325pa	__AVR_ATmega325PA__
avr5	atmega3250	__AVR_ATmega3250__
avr5	atmega3250a	__AVR_ATmega3250A__
avr5	atmega3250p	__AVR_ATmega3250P__
avr5	atmega3250pa	__AVR_ATmega3250PA__
avr5	atmega328	__AVR_ATmega328__
avr5	atmega328p	__AVR_ATmega328P__
avr5	atmega329	__AVR_ATmega329__
avr5	atmega329a	__AVR_ATmega329A__
avr5	atmega329p	__AVR_ATmega329P__
avr5	atmega329pa	__AVR_ATmega329PA__
avr5	atmega3290	__AVR_ATmega3290__
avr5	atmega3290a	__AVR_ATmega3290A__
avr5	atmega3290p	__AVR_ATmega3290P__
avr5	atmega3290pa	__AVR_ATmega3290PA__
avr5	atmega32c1	__AVR_ATmega32C1__
avr5	atmega32hvb	__AVR_ATmega32HVB__
avr5	atmega32hvbrevb	__AVR_ATmega32HVBREVB__
avr5	atmega32m1	__AVR_ATmega32M1__
avr5	atmega32u4	__AVR_ATmega32U4__
avr5	atmega32u6	__AVR_ATmega32U6__
avr5	atmega406	__AVR_ATmega406__
avr5	atmega644rfr2	__AVR_ATmega644RFR2__
avr5	atmega64rfr2	__AVR_ATmega64RFR2__
avr5	atmega64	__AVR_ATmega64__
avr5	atmega64a	__AVR_ATmega64A__
avr5	atmega640	__AVR_ATmega640__
avr5	atmega644	__AVR_ATmega644__
avr5	atmega644a	__AVR_ATmega644A__
avr5	atmega644p	__AVR_ATmega644P__
avr5	atmega644pa	__AVR_ATmega644PA__
avr5	atmega645	__AVR_ATmega645__
avr5	atmega645a	__AVR_ATmega645A__
avr5	atmega645p	__AVR_ATmega645P__
avr5	atmega6450	__AVR_ATmega6450__
avr5	atmega6450a	__AVR_ATmega6450A__
avr5	atmega6450p	__AVR_ATmega6450P__
avr5	atmega649	__AVR_ATmega649__
avr5	atmega649a	__AVR_ATmega649A__
avr5	atmega6490	__AVR_ATmega6490__
avr5	atmega6490a	__AVR_ATmega6490A__
avr5	atmega6490p	__AVR_ATmega6490P__
avr5	atmega649p	__AVR_ATmega649P__
avr5	atmega64c1	__AVR_ATmega64C1__
avr5	atmega64hve	__AVR_ATmega64HVE__
avr5	atmega64m1	__AVR_ATmega64M1__

In that you have everything from 16K to 64K models with RAM sizes from 1K and EEPROM sizes from 512 bytes to RAM of 4K and EEPROM of 2K.

 

But this all has to be done with just one avr5.x file. So back in the WinAVr days that file just said:

MEMORY
{
  text      (rx)   : ORIGIN = 0, LENGTH = 128K
  data      (rw!x) : ORIGIN = 0x800060, LENGTH = 0xffa0
  eeprom    (rw!x) : ORIGIN = 0x810000, LENGTH = 64K
  fuse      (rw!x) : ORIGIN = 0x820000, LENGTH = 1K
  lock      (rw!x) : ORIGIN = 0x830000, LENGTH = 1K
  signature (rw!x) : ORIGIN = 0x840000, LENGTH = 1K
}

So this allowed for up to 128K of flash, a little under 64K of RAM and 64K of EEPROM. This meant that it worked for all devices.

 

But if you took a mega16 say (16K flash, 1K SRAM 0.5K EEPROM) then if you caused 3K of RAM usage or 1.3K of EEPROM usage or something then the linker would NOT throw an error. What you could, of course, do if you only ever planned to work with mega16 was to "hack" this file and put mega16 limits in it:

{
  text      (rx)   : ORIGIN = 0, LENGTH = 16K
  data      (rw!x) : ORIGIN = 0x800060, LENGTH = 1K
  eeprom    (rw!x) : ORIGIN = 0x810000, LENGTH = 512

So now if you tried to link a program with 3K RAM usage or 1.3K of EEPROM it would throw a linker error. But editing core system files is never wise. So the usual idea would be to copy this avr5.x to a local avrmeg16.x or something then build the code with -Wl,-T,avrmeg16.x so this locally modified linker script was used in preference to the default avr5.x when building for -mmcu=atmega16

 

But then, in the last few years files such as avr5.x were modified to now use (as you find in AS7/avr-gcc 5.4):

OUTPUT_ARCH(avr:5)
__TEXT_REGION_LENGTH__ = DEFINED(__TEXT_REGION_LENGTH__) ? __TEXT_REGION_LENGTH__ : 128K;
__DATA_REGION_LENGTH__ = DEFINED(__DATA_REGION_LENGTH__) ? __DATA_REGION_LENGTH__ : 0xffa0;
__EEPROM_REGION_LENGTH__ = DEFINED(__EEPROM_REGION_LENGTH__) ? __EEPROM_REGION_LENGTH__ : 64K;
__FUSE_REGION_LENGTH__ = DEFINED(__FUSE_REGION_LENGTH__) ? __FUSE_REGION_LENGTH__ : 1K;
__LOCK_REGION_LENGTH__ = DEFINED(__LOCK_REGION_LENGTH__) ? __LOCK_REGION_LENGTH__ : 1K;
__SIGNATURE_REGION_LENGTH__ = DEFINED(__SIGNATURE_REGION_LENGTH__) ? __SIGNATURE_REGION_LENGTH__ : 1K;
__USER_SIGNATURE_REGION_LENGTH__ = DEFINED(__USER_SIGNATURE_REGION_LENGTH__) ? __USER_SIGNATURE_REGION_LENGTH__ : 1K;
MEMORY
{
  text   (rx)   : ORIGIN = 0, LENGTH = __TEXT_REGION_LENGTH__
  data   (rw!x) : ORIGIN = 0x800060, LENGTH = __DATA_REGION_LENGTH__
  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = __EEPROM_REGION_LENGTH__

That very "C like" structure using a ternary operator is effectively saying "if nothing else is defined just use the "big" defaults as before" but this gives the opportunity for __TEXT_REGION_LENGTH__. __EEPROM_REGION_LENGTH__ to be externally defined and passed in.

 

The only thing was that I'm not sure if anything currently tries to do that. If I do this in AS7:

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

EEMEM uint8_t big_array[2048];

int main(void) {
}

then build for mega16 I get a build that shows:

	Task "RunOutputFileVerifyTask"
				Program Memory Usage 	:	124 bytes   0.8 % Full
				Data Memory Usage 		:	0 bytes   0.0 % Full
				EEPROM Memory Usage 	:	2048 bytes   400.0 % Full	(Memory Overflow)

but the build completes without error. But now, knowing what I now know from Chuck's post #5 I build with -Wl,--defsym=__EEPROM_REGION_LENGTH__=512 I immediately get:

 

 

So the "magic" here is to use --defsym to define the REGION_LENGTHs to the right values for the chip you are building for.

 

PS I just checked it's EIGHTEEN not 17 different:

 Directory of C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\lib\ldscripts

12/09/2017  23:24             8,155 avr1.x
12/09/2017  23:24             8,160 avr2.x
12/09/2017  23:24             8,161 avr25.x
12/09/2017  23:24             8,162 avr3.x
12/09/2017  23:24             8,163 avr31.x
12/09/2017  23:24             8,162 avr35.x
12/09/2017  23:24             8,160 avr4.x
12/09/2017  23:24             8,162 avr5.x
12/09/2017  23:24             8,163 avr51.x
12/09/2017  23:24             8,163 avr6.x
12/09/2017  23:24             7,637 avrtiny.x
12/09/2017  23:24             8,165 avrxmega1.x
12/09/2017  23:24             8,165 avrxmega2.x
12/09/2017  23:24             8,253 avrxmega3.x
12/09/2017  23:24             8,165 avrxmega4.x
12/09/2017  23:24             8,165 avrxmega5.x
12/09/2017  23:24             8,165 avrxmega6.x
12/09/2017  23:24             8,165 avrxmega7.x
              18 File(s)        146,491 bytes

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

The Arduino build compares the size of your binary with the available application memory.    And whinges when appropriate.

 

The "application area size" is specified in boards.txt

If you create a custom board with a bigger/smaller bootloader,  you modify the size.

 

Yes,  the simple avrdude command will only look at your memory.   It will not care if you attempt to overwrite the bootloader.

But it would notice if the bootloader does not verify (because the lockbits have been set to protect the boot area)

 

David.

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

david.prentice wrote:
The "application area size" is specified in boards.txt
Which is kind of the point - to police the size limits for 300+ AVR you need a "database" that contains the size limits (flash, RAM and EEPROM) of all the devices which becomes a bit of a maintenance nightmare which is why these "more than big enough" limits were initially defined.

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

No, avrdude already polices flash and eeprom. It will whinge if you exceed the size.
.
It is only bootloader uploading that worries about the locations. Any respectable bootloader will check the location. And a stupid bootloader will be protected by lock bits.
A verify stage will detect bad programming.
Yes, a "boards.txt" entry aborts the build before going to avrdude.
You can use a post-build command in AS7 to abort if avr-size fails.
.
A hardware programmer can detect a programming problem in the programming phase but avrdude chooses not to. It relies on verify.
.
Most punters will either erase chip and program the whole flash.
Or use a bootloader to only program specific areas. The bootloader knows the safe areas.
.
Of course occasionally you program without erasing the chip. Likewise punters enjoy writing RSTDISBL fuse or other ways to shoot their feet.
.
David.

Last Edited: Thu. Feb 7, 2019 - 04:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This isn't my area.  But I'm getting confused.

ChuckH wrote:
I didn't like modifying the .x files since when I move to a different processor I would have to remember research and modify a different .x file.

Fair enough--the search for a "generic" solution. Yet the "solution"

 

ChuckH wrote:
I modified my "Other Linker Flags" in the project properties to: -Wl,--cref,--defsym=__TEXT_REGION_LENGTH__=512 (The 512 is obviously wrong but I wanted to force the build to fail as a test)

...would require a change to the line "when I move to a different processor", wouldn't it?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

The header files for each AVR have a section with memory sizes.

for example: /usr/lib/arv/io8535.h it looks like:

 

/* Constants */
#define RAMSTART   0x60
#define RAMEND     0x25F    /*Last On-Chip SRAM location*/
#define XRAMEND    RAMEND
#define E2END      0x1FF
#define E2PAGESIZE 0
#define FLASHEND   0x1FFF

In the startup code the stackpointer has to be initialised properly and that needs RAM size, or is that also just one of those 17 variants?

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com