# Moving Data, Bss and heap memory to external SRAM Atmega2560

This is my first question here, sorry if I miss something.

I’m using an Atmega2560 as main processor for my project but I’m running out of ram so I have connected an external 64 KB (64 x 8) SRAM to it. I searched around and found couple of projects and libraries that use the external sram:

http://andybrown.me.uk/wk/2011/08/28/512kb-sram-expansion-for-the-arduino-mega-design/

But all of these use the external SRAM as heap memory.

I would like to use the external memory as data, bss, and heap memory leaving the entire SRAM for stack.

I found this in the avr-libc manual: https://www.nongnu.org/avr-libc/user-manual/malloc.html

Using the information from the manual, I changed the makefile for Arduino Mega bootloader here: C:\Program Files (x86)\Arduino\hardware\arduino\avr\bootloaders\stk500v2

I changed “EXTMEMOPTS =“ to this:

EXTMEMOPTS = -Wl,--section-start,.data=0x802200,--defsym=__heap_end=0x80ffff

0x800000 is the offset for Harvard architecture, 0x802200 is the first address for the external memory.

I’m recompiling the bootloader with the new make file but I get the following error:

I believe it’s complaining because the address is outside the internal SRAM, I couldn’t find any way to fix it.

I’m not sure what’s wrong, what am I missing ? Also if this gets solved Is it okay to initialise the external memory hardware (by setting XMCRA and XMCRB registers) in bootloader’s main function? If not where should I do it ?

Update: Here is the linker script: https://codeshare.io/aJom3K

Update 2: The first problem is solved now; I was editing the wrong ld file and after changing the correct file to the following It worked:

DATA_REGION_ORIGIN = 0x802200;

The next problem is where to initialize the hardware for external SRAM ? I'm currently doing this in __jumpMain function of the bootloader which also sets the stack pointer register. Is this fine or should I do it somewhere else ?

Update 3: As mentioned by @Brian Drummond in the partial answer, I changed the linker args to this:

EXTMEMOPTS = -Wl,--section-start,.data=0x802200,--defsym=__heap_end=0x80ffff,--defsym=DATA_REGION_ORIGIN=0x802200

and reverted the changes to the build script to avoid problems in the future.

For anyone doing something similar: You also need to add args for changing the DATA_REGION_LENGTH to the new length otherwise, it will only use the old length which in my case is: 0xfe00.

Update 4: After some testing, apparently the MC is not using the external SRAM; after programming it with the new script I can disconnect the SRAM and it will continue to work fine. Also I checked the data and address lines to the SRAM and nothing is being written or read.

So I'm back at the beginning, Any ideas what's wrong ?

• what's the .ld file look like? – user_1818839 Feb 26 at 20:35
• hate to say it, but if your firmware needs dynamic allocation and external SRAM, the AtMega might not be the right chip for the job – Pete W Feb 26 at 21:04
• Here is the linker script file: codeshare.io/aJom3K – Oli Feb 26 at 21:56
• @PeteW I know I’m pushing it and I will move to arm in the future but I want to solve this problem first. – Oli Feb 26 at 21:58
• @BrianDrummond Thank you so much, I was changing the wrong ld file all this time. After you mentioned it I realized it. I removed the condition and changed the ld file to this: DATA_REGION_ORIGIN = 0x802200; and it solved the problem. But I'm not sure where was it being defined originally, since if I let the condition to be there It gives me the same error. – Oli Feb 27 at 7:59

TL;DR This will not work the way you want because the Atmega2560 does not have an external memory interface.

When you create a variable that gets stored in [internal] SRAM, it is possible for the processor's ALU to fetch that variable because the ALU and the SRAM are connected to the same internal memory bus. On higher-end processors (eg. STM32F756), you sometimes have a dedicated external-memory interface peripheral that can, for example, connect to SRAMs, DRAMs, SDRAMS, etc. These peripherals are special, because they convert the internal memory bus protocol of the processor into the external hardware interface needed to load/store data to an external memory chip.

A simple[r] analogy is this: Your ATmega2560 has an SPI peripheral. When you configure that as an SPI Master and write something to the SPI Data-Register, the peripheral automagically clocks the bits out onto the bus with no further CPU intervention. You have translated a memory access (the data-register write) into a physical protocol.

Conversely, you could write code that would manually wiggle the IO pins to achieve the same hardware result as the SPI peripheral. This has a number of limitations, the main one being that you have to manually unpack a byte of data and set the pins at the appropriate times manually (via code). It should be obvious that this code is, under the hood, doing many memory reads and writes and you no longer have the automatic conversion of "write to data register, clock out bits" like with the dedicated SPI peripheral.

What you are trying to implement with your external SRAM and linker script fiddling is not going to work because as far as the microcontroller and the linker are concerned, that address space simply does not exist, and there is no hardware peripheral that can convert bus instructions into external memory signals. The projects you linked are simply using a software layer to manually control the IO pins to talk to the external SRAM (aka bit-banging).

Edit to add: You can still totally use an external SRAM, it just won't be automatic. You'll need a software library to make it work, and you'll have to explicitly store/load data through an API of some kind. If you are running out of SRAM and your program cannot be linked then you are out of luck. There are some extremely hacky ways around this but I do not recommend them. Instead either decrease the memory requirements of your application until it can be linked, or switch to a beefier microcontroller. I would recommend the latter, and say get rid of the external memory because even on a processor with an external memory interface, adding external memory is serious adder in cost, complexity, and time.

• Thanks for clarifying it, I was confused by the datasheet taking about the XMEM interface (External Memory Interface) and how it handles everything automatically also the AVR-LIBC documentation made it look like it's possible to do so; the system I'm working on is just a prototype and I will upgrade it to an ARM, since as you have mentioned, It's much more efficient and simpler. – Oli Mar 1 at 20:50

Looking at the .ld file (linker script) we can see

__DATA_REGION_ORIGIN__ = DEFINED(__DATA_REGION_ORIGIN__) ? __DATA_REGION_ORIGIN__ : 0x800200;

which seems to not agree with your desired .data=802200 on the command line. You can trace the path from __DATA_REGION_ORIGIN__ to .data in the .ld script itself.

Which suggest you can set __DATA_REGION_ORIGIN__ in the linker args (on the ld command line), and (if set) it will override the value 0x800200 given in the .ld script.

Or edit the expression in the linker script. Simply editing the value ought to work (in my understanding) but apparently something else (but WHAT?) in the ld process is supplying a value which overrides the script value. I feel the .ld process is unnecessarily obscure and I don't claim to fully understand it.

As a workaround, deleting the conditional expression leaving only the value gets you going, but you have to be careful with "unofficial" changes to build scripts, they tend to cause maintenance problems later.

Adding -v to the ld command may yield further information showing what is really going on...

• This answer misses the point that the ATMega2560 has no means of memory-mapping external SRAMs, and no amount of linker-script fiddling will fix that. If anything, that will just break the linkage and nothing will work. – Brendan Simpson Mar 1 at 20:27