Using external sram for .data section

24 views
Skip to first unread message

hcaltenco

unread,
Apr 16, 2011, 7:43:04 AM4/16/11
to Micropendous
Hello all,

I am having a problem storing the ".data" section in the external ram
of the micropendous board. I hope someone here can help me, since I
have been looking in forums and no one seems to have had the same
problem.

I have an application that uses 50 kb of static data, therefore needs
external ram. Which is not a problem since the micropendous boards
come very well equiped ;). However, I was quite surprised that the
default makefiles in the micropendous distribution and other examples
(i.e. latest LUFA distribution) give the linker external memory
options that do not work.

The "-Tdata" is not recognized by the linker. Give it a try, try to
compile one of the examples (e.g. MicropendousKeyboardTest) using the
default " -Wl,-Tdata=0x802100,--defsym=__heap_end=0x80ffff " for the
at90usb1287. Then open the .lss or .map files and you will see that
the data section starts at the internal address 0x800100. The correct
extmemoptions for the linker should be: "-Wl,--section-
start,.data=0x802100,--defsym=__heap_end=0x8090ff ", as stated in the
avr-libc documentation (http://www.nongnu.org/avr-libc/user-manual/
malloc.html). Compiling with these flags will produce lss and map
files with the correct external address starting at 0x802100. It took
me some time to find out that, and will probably save many people that
use external sram some time if this was corrected in all the
distribution examples.

Anyway, then you are just supposed to initialize external memory in
the hardware initialization or at the beginning of your main() as:
/* Enable External SRAM */
DISABLE_VOLTAGE_TXRX;
PORTE_EXT_SRAM_SETUP;
ENABLE_EXT_SRAM;
XMCRA = ((1 << SRE)); // enable external SRAM with 0 wait
states
XMCRB = 0;
//__malloc_heap_start = (void*) 0x5100; // make sure that
your heap starts after your data and bss
//__malloc_heap_end = (void*) 0x90ff;

/*Allocating external memory using __malloc_heap_start and
__malloc_heap_end do not seem to have any effect on any of the four
options described below (thats why I commented them).*/

However this does not solve the problem completely. Try it out for
example in the "MicropendousKeyboardTest" example. Setting all the
data, bss, and heap to the external does not type anything when you
ground a pin with a cable:
EXTMEMOPTS += -Wl,--section-start,.data=0x802100,--
defsym=__heap_end=0x8090ff

Setting the data section in the external and the rest on the internal
does not type anything either:
EXTMEMOPTS += -Wl,--section-start,.data=0x802100,--section-
start,.bss=0x800100
EXTMEMOPTS += --defsym=__heap_start=__bss_end,--
defsym=__heap_end=0x8010ff

But Setting the bss section in external and the rest on the internal
works (types continuously a2 and a0 because the external memory
registers need to have that pins in low).
EXTMEMOPTS += -Wl,--section-start,.data=0x800100,--section-
start,.bss=0x802100
EXTMEMOPTS += --defsym=__heap_start=__data_end,--
defsym=__heap_end=0x8010ff

Also Setting only the heap to external ram and the rest in internal
also works (typing a2 and a0 continuously).
EXTMEMOPTS += --defsym=__heap_start=0x802100,--
defsym=__heap_end=0x8090ff

Due to this issue, it seems that the .data section cannot be located
in external ram!!! Which is a bit annoying since is the 50kb section i
need to put there. The .bss and the heap seem to have no problem being
in the external memory.

I am suspecting it has something to do with the timings, since it
takes two clock cycles to write and read from external ram while it
takes only one clock cycle to write and read from internal ram. Has
anyone been successful using the data section in external memory?

I would appreciate very much the help, this problem is driving me
crazy.

All the best,

Opendous Inc.

unread,
Apr 16, 2011, 9:30:56 PM4/16/11
to Micropendous
Please try testing the external memory with SRAM_Test which checks
reading and writing to the external SRAM with both pattern tests and
random data and will at least confirm SRAM is functional:
http://code.google.com/p/micropendous/wiki/SRAM_Test

Are you using the latest version of gcc-avr or WinAVR?

>__malloc_heap_start = (void*) 0x5100;

You should not need to define heap_start manually.

Do you require malloc?

Consider editing SRAM_Test which does edit the memory locations or
use malloc. It simply accesses external SRAM through the AVR's EXTMEM
peripheral. All the memory sections are at their default locations
and limits. You can use ExtMemArray[itemIndex] which is a 56kbyte
char/uint8_t array mapped to external SRAM. If you then select the
other SRAM bank (i.e, SELECT_EXT_SRAM_BANK1) you can use the exact
same array (ExtMemArray[itemIndex]) to access the other 56kbytes.
http://code.google.com/p/micropendous/source/browse/trunk/Micropendous/Firmware/SRAM_Test/SRAM_Test.c

>open the .lss or .map files and you will see that
>the data section starts at the internal address 0x800100.

Check to make sure you have a line in the makefile that actually
adds to the linker command such as:
LDFLAGS += $(EXTMEMOPTS)

>Setting all the data, bss, and heap to the
>external does not type anything

Assuming SRAM initialization (XMCRA=... ) runs properly that is
indeed strange behavior. Moving .data to external SRAM moves
everything including local variables so only if it is not enabled
would no function run.

My only plausible guess is that .bss variables are not initialized
and something reads an incorrect value from external SRAM. This would
imply external SRAM is not being initialized, which makes sense as
the .init sections run before main() and only in main() is external
SRAM enabled.
http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

>Has anyone been successful using
>the data section in external memory?

SRAM_Test does successfully use the external SRAM but only because
it is really just a hack to make use of almost the full array
(112kbytes).

I will look into writing a quick demo for a 50kbyte array using
malloc. Do you compute/create your 50kbyte array or will it be copied
from FLASH?

Opendous Support

unread,
Apr 17, 2011, 2:45:20 AM4/17/11
to Micropendous
I seem to have copied the wrong linker command into/from the
makefile. -T will never work and --section-start must be used:
http://www.nongnu.org/avr-libc/user-manual/malloc.html

I will finish up a demo based on malloc() and update the Micropendous
distribution accordingly.

hcaltenco

unread,
Apr 17, 2011, 9:40:31 AM4/17/11
to Micropendous
Thank you very much for the fast reply.

> Please try testing the external memory with SRAM_Test which checks
> reading and writing to the external SRAM with both pattern tests and
> random data and will at least confirm SRAM is functional

Actually, I did test the external memory using the SRAM_Test, one of
my first suspicions was that the external memory was faulty. But the
SRAM_Test worked. However as it does not use malloc() or the linker to
allocate sections in the external memory, I could not use the example
further.

>   Are you using the latest version of gcc-avr or WinAVR?
This was a good suggestion, I am using winavr_20100110 (i believe is
the latest) but in my older computer i had winavr_20090313. I have
just tested compiling there but the results are the same :(

>  You should not need to define heap_start manually.
>   Do you require malloc?

You are right, it should not be needed, the linker is already doing
that. Anyway I have tried with and without manual definition, and
allocating external memory using __malloc_heap_start and
__malloc_heap_end do not seem to have any effect.

>   Consider editing SRAM_Test which does edit the memory locations or
> use malloc.  It simply accesses external SRAM through the AVR's EXTMEM
> peripheral.  All the memory sections are at their default locations
> and limits.  You can use ExtMemArray[itemIndex] which is a 56kbyte
> char/uint8_t array mapped to external SRAM.  If you then select the
> other SRAM bank (i.e, SELECT_EXT_SRAM_BANK1) you can use the exact
> same array (ExtMemArray[itemIndex]) to access the other 56kbytes.

I understand, the problem is that I don't want to use the external
memory to manually allocate arrays. What I have is a "library.a" that
contains linguistic databases that I only have access through the
library's API. That when loaded seems to get copied to the .data
section, and that is why my .data section is so large. I dont know how
to manage it otherwise.

> Check to make sure you have a line in the makefile that actually
> adds to the linker command such as:
> LDFLAGS += $(EXTMEMOPTS)

Yes, no problem here, I did not modify that from the makefile :)

> Assuming SRAM initialization (XMCRA=... ) runs properly that is
> indeed strange behavior.  Moving .data to external SRAM moves
> everything including local variables so only if it is not enabled
> would no function run.

Yes, I have checked the PORTE_EXT_SRAM_SETUP and the ENABLE_EXT_SRAM
in UsefulMicropendousDefines.h to see that the registers are modified
correctly as in the at90usb1287 documentation, and they are, so it is
not a problem with the SRAM initialization. Everything needed to be
done is done correctly there.
The strangest thing is that moving the .bss and/or the heap to SRAM
seems to work. Is only a problem when moving the .data (or all
sections). Which makes me thing that the problem is in moving
the .data. I am also thinking that this might be a problem with the
lufa library???

>   My only plausible guess is that .bss variables are not initialized
> and something reads an incorrect value from external SRAM.  This would
> imply external SRAM is not being initialized, which makes sense as
> the .init sections run before main() and only in main() is external
> SRAM enabled.

No, there is no problem with the initialization of the.bss variables
because I have also tried moving only the .data section leaving
the .bss section in internal memory:
>> EXTMEMOPTS += -Wl,--section-start,.data=0x802100,--section-start,.bss=0x800100
>> EXTMEMOPTS += --defsym=__heap_start=__bss_end,--defsym=__heap_end=0x8010ff
the .lss and .map files show that the .bss is in internal memory and
the program does not work.
The strangest thing is that the program DOES work if I move the bss to
external leaving the data section in internal. It also works if I only
move the heap, or if I move both the bss and the heap.
>> EXTMEMOPTS += -Wl,--section-start,.data=0x800100,--section-start,.bss=0x802100
>> EXTMEMOPTS += --defsym=__heap_start=__data_end,--defsym=__heap_end=0x8010ff


> SRAM_Test does successfully use the external SRAM but only because
> it is really just a hack to make use of almost the full array
> (112kbytes).
Hehe... yes, as you well mention it, most probably is because you are
manually controlling memory banks and writing / reading arrays
directly from/to the SRAM

> I will look into writing a quick demo for a 50kbyte array using malloc.

Thank you very much, this would be very very helpful, I am most
probably doing something wrong, but I cannot figure out what. I have
been programming AVRs for a couple of years but this is the first time
I use the external SRAM, so no wonder I feel lost. And as I don't find
anyone with the same problem in forums it makes me think I am missing
out something very simple.

> Do you compute/create your 50kbyte array or will it be copied
> from FLASH?

What I have is a "library.a" that contains linguistic databases that I
only have access through the library's API. That when loaded seems to
get copied from flash to the .data section, and that is why my .data
section is so large. The problem is that I dont have access to the
database arrays directly, so I cannot use progmem to tell them to stay
in flash. If I do, I would have to modify the way the functions and
callbacks are called in the API, and I would really like to avoid
that.

Thanks a lot for the help

hcaltenco

unread,
Apr 17, 2011, 9:45:00 AM4/17/11
to Micropendous
Yes, hehe... and many other makefiles I have seen also have the wrong
linker command, including all the LUFA library examples. I will write
Dean about it so he corrects it in future LUFA distributions.

A demo that uses the linker and malloc to allocate the data and
possibly also the bss and heap sections will be very helpful. Thank
you very much.

On Apr 17, 8:45 am, Opendous Support <opendous.supp...@gmail.com>
wrote:

Opendous Support

unread,
Apr 18, 2011, 3:12:22 AM4/18/11
to microp...@googlegroups.com
The attached USBVirtualSerial-ExtSRAMTest firmware is a complete
LUFA-based example of how to use malloc() and its associated linker
commands to allocate and access a 32-bit integer array in external
SRAM on the Micropendous boards and communicate with the user over
USBVirtualSerial using stdlib printf functions.

Either compile it from scratch or load
USBVirtualSerial-ExtSRAMTest_Micropendous-AT90USB1287.hex directly
onto your board. A 16MHz crystal is assumed.

The Host side testing script, TestExtSRAMAccess.py, requires Python
2.5.x and PySerial
http://python.org/download/releases/2.5.5
http://sourceforge.net/projects/pyserial/files/pyserial/2.5/

If you do not want to install Python the communication protocol is
very simple. Send some data to the Serial Port of the device loaded
with USBVirtualSerial-ExtSRAMTest firmware and read back seven lines
of \r\n-terminated text.

The output should be something like:
PORTE=135


__malloc_heap_start=8448

__malloc_heap_end=65535


ExtMemArray[0]=4294967295 at 8450

ExtMemArray[1]=1010101010 at 8454

ExtMemArray[ExtMemLastIndex]=1234567890 at 65506


ExtMemLastIndex=14264

Changes made to USBVirtualSerial.c from Micropendous-2011-03-01.zip
to create USBVirtualSerial-ExtSRAMTest.c include:

The required linker command in the makefile is:
EXTMEMOPTS +=-Wl,--defsym=__heap_start=0x802100,--defsym=__heap_end=0x80ffff
This places the heap in external SRAM while leaving internal SRAM
for variables and the stack. Other options given at
http://www.nongnu.org/avr-libc/user-manual/malloc.html are not
workable.

Set up external SRAM in an .init section:
// set up external SRAM prior to anything else to make sure malloc()
has access to it

void EnableExternalSRAM (void) __attribute__ ((naked)) __attribute__
((section (".init3")));

void EnableExternalSRAM(void)

{
PORTE_EXT_SRAM_SETUP;

XMCRA = ((1 << SRE)); // zero wait-states

XMCRB = 0;
}

Create a global pointer to the data array:
static uint32_t* ExtMemArray;

Use malloc to allocate the memory for 32-bit integers near the top of main()
uint16_t ExtMemArraySize = 57056; // 57056/4 = 14264

ExtMemArray = (uint32_t *) malloc(ExtMemArraySize);


if (ExtMemArray == NULL) {
abort(); }

Before the call to USB_Init() in SetupHardware, make sure once more
that the external SRAM interface is active:
PORTE_EXT_SRAM_SETUP;

ENABLE_EXT_SRAM;

SELECT_EXT_SRAM_BANK0;

Inside MainTask() change the values of array elements and send
associated data to the Host if it requests it:
ExtMemArray[0] = (uint32_t)4294967295;
// maximum unsigned 32-bit integer value
ExtMemArray[14264] = (uint32_t)1234567890; // final array element
if ((count = fread(&buffer, 1, CDC_TXRX_EPSIZE, &USBSerialStream)) > 0) {
fprintf(&USBSerialStream, "__malloc_heap_start=%5u\r\n",
((uint16_t)__malloc_heap_start));
fprintf(&USBSerialStream, "ExtMemArray[0]=%10lu at %5u\r\n",
((uint32_t)ExtMemArray[0]), ((uint16_t)&ExtMemArray[0]));
fprintf(&USBSerialStream, "ExtMemArray[14264]=%10lu at %5u\r\n",
((uint32_t)ExtMemArray[14264]), ((uint16_t)&ExtMemArray[14264]));
}

USBVirtualSerial-ExtSRAMTest firmware can be found at:
http://code.google.com/p/micropendous/downloads/detail?name=USBVirtualSerial-ExtSRAMTest.zip

USBVirtualSerial-ExtSRAMTest.zip
Reply all
Reply to author
Forward
0 new messages