Electronic – Moving Data, Bss and heap memory to external SRAM Atmega2560

atmegaavrembeddedprogramming

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/

https://hackaday.io/project/21561-arduino-mega-2560-32kb-ram-shield

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

Link to original makefile:
https://github.com/arduino/ArduinoCore-avr/blob/master/bootloaders/stk500v2/Makefile

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:

enter image description here

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 ?

bootloader: https://codeshare.io/5zNr1b

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 ?

Best Answer

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.