There are some sketches shown for basic testing and check out of the board. Seems that using pointers are used to access this added SRAM, but the Arduino reference section is silent on using pointers. I certainly can and will look for other references and try and get comfortable trying to use pointers, but I was wondering if there are methods or tricks to try and 'hide' or simplify reading and writing variables and arrays using extended ram? Could use of some defines or macros make using the ram look a little more like using normal variables, integers, longs, floats, arrays, strings, maybe even structures, rather then just basic byte transfeers via pointers?
I guess I'm just looking for hints and ideas on how best one can utilize this nice new addition to the arduino world.
What aspects of pointers are you enquiring about? General info, or how to try to hide that you are using them in the first place?
Certainly you can make pointers to structures. It would seem logical to me to get the dynamic memory allocation to use this new RAM, not sure how to go about that.
Glancing over the example code, it looks like he is "manually" allocating pointers to the extra RAM. Certainly this is one way you could do it, particularly if you don't need to reclaim it later.
You might make your own dynamic memory allocation (or probably borrow the existing one). The most logical would be to find how the normal malloc/free libraries "get started" and then redirect them to the much larger pool of memory you have available.
Once you have a lot of dynamic memory available, if I may recommend, the STL library is very good for organizing stuff. Vectors, maps, lists etc.
And with a bit of fooling around you can coerce a pointer into a reference, and then it looks (more) like a "normal" variable (or structure).
Yeah, that's the key... having malloc() and free() (or extended SRAM-aware analogues to it, which might not be hard to write yourself) aware of it. That type of technique will only realistically be good for the first 64KB, 'cause once you implement paging the second 64KB segment (or go with a technique of paging 32KB into the upper half of the 64KB address space so you're not wasting the lower ~8K of the SRAM due to the built-in SRAM occupying that space) you're definitely managing the memory usage within your own app anyway.
On the other hand I gotta believe there's something hooked into the avr-gcc compiler to denote how much SRAM you have available for when it compiles... or maybe there isn't? The question is what happens when you malloc() above the limit of the built-in SRAM; if the avr-gcc supplied libc doesn't bother to check where the upper limit is, then it's possible the built in malloc() and free() already support the whole 64KB address space natively. That'll limit you to only using 56KB of the extra memory, since you won't want to take advantage of paging (your pointers will become meaningless once you page out that SRAM segment) but it's still a win.
Well I like reading the responses here so far. I just hope this product generates enough interest so you more proficient software gurus can help blaze the trail for some of us hardware knuckle draggers. Hell I think it would look cool enough just being plugged in, even if I can't use it very effectively. Kind of like adding chrome tail pipe tips.
From my reading of this link, if you want to use external RAM seamlessly, you're going to need to adjust the linker options. I'm not sure that's possible with the Arduino IDE. So to achieve what you're after, either the Arduino IDE needs to be modified to allow linker options or you need to switch to a different IDE (Eclipse, AVR Studio 5, command line etc).
If the heap is going to be moved to external RAM, __malloc_heap_end must be adjusted accordingly. This can either be done at run-time, by writing directly to this variable, or it can be done automatically at link-time, by adjusting the value of the symbol __heap_end.
So in setup you adjust __malloc_heap_end, before doing any dynamic memory allocation.
That's a very good question. I doubt changing __malloc_heap_end would do it. Especially in setup, because if you changed the stack pointer, and setup exited, it would exit to a very strange place.
In fact after looking at the Optiboot code I can't quite see how the stack pointer gets set up. According to that:
/* Assumptions: */
/* The code makes several assumptions that reduce the */
/* code size. They are all true after a hardware reset, */
/* but may not be true if the bootloader is called by */
/* other means or on other hardware. */
/* No interrupts can occur */
/* UART and Timer 1 are set to their reset state */
/* SP points to RAMEND */
But how can a hardware reset happen to know how much memory is installed, and set up SP accordingly? Perhaps, per processor, that figure is hard-wired in (the memory in the core chip that is). Or it might set the SP to 0xFFFF and rely on partial address decoding to work.
In fact, adjusting linker options would hardly help here, as the Optiboot (for one anyway) doesn't attempt to set the stack pointer. And you might ask, what if you aren't using a boot loader anyway?
The stack grows from high to low, so setting it up nice and high would seem sensible, otherwise making the heap end higher will simply make the heap grow into the stack at the 2 Kb mark.
I'm not sure about this, and when the expanded RAM card I ordered arrives I can do some testing. But it will be trial-and-error I think.
IIRC the stack can't be in external RAM, hardware traps load and store operations but that's all I think. Anyway the SP is only a 12bit (depending on the processor) reg.
As to who sets the SP, someone has to :), I know when writing AVR assembler you have to set it up yourself, the hardware doesn't know.
; Set Stack Pointer to top of RAM
ldi r16, LOW(RAMEND)
ldi r17, HIGH(RAMEND)
out SPL, r16
out SPH, r17
Hmm, interesting. I suppose there's no reason is can't be hardwired in silicon. RAMEND is a constant known to the compiler/assembler/linker/whatever, as you can see I've used it in the ASM code. It seems I didn't have to.
The Stack in the data SRAM must be defined by the program before any subroutine calls are
executed or interrupts are enabled. Initial Stack Pointer value equals the last address of the
internal SRAM and the Stack Pointer must be set to point above start of the SRAM,
That doesn't totally contradict what page 13 says.
As I read it "Initial Stack Pointer value equals the last address of the internal SRAM" - as I said above. So the processor designers (appear to) make the reset automatically load SP with what they know they have designed-in as the last address in internal RAM. That makes sense, because what other value would be useful?
Then this: "The Stack in the data SRAM must be defined by the program before any subroutine calls are executed or interrupts are enabled.".
Now I read that as "the SP must be set before calling subroutines or enabling interrupts". Which makes sense. And the default setting is an acceptable setting.
However if you want to change it you must do so before calling subroutines or enabling interrupts.
Thus, doing it in setup won't be such a good idea - unless you never leave setup - which is quite possible to do.
Whether or not that is totally correct, it makes a certain amount of sense. So the logical thing to do in setup is to move the heap start and end, and leave the stack where it is. Thus you have 8 Kb of RAM (on an Atmega2560) which you can use for static variables, or the stack (and thus auto variables). But you then get whatever the external RAM gives you (56 Kb or whatever) for dynamic memory.
Accessing external SRAM takes one additional clock cycle per byte compared to access of the internal SRAM. This means that the commands LD, ST, LDS, STS, LDD, STD, PUSH, and POP take one additional clock cycle. If the Stack is placed in external SRAM, interrupts, subroutine calls and returns take three clock cycles extra because the three-byte program counter is pushed and popped, and external memory access does not take advantage of the internal pipe- line memory access.
(my emphasis)
So it appears perfectly OK to put the stack in external RAM, if you don't mind the overhead.
But you then get whatever the external RAM gives you (56 Kb or whatever) for dynamic memory.
So I get that the C language (or C standard library?) can allow us to use heap memory using the malloc() function. Can we also use the C++ dynamic memory functions new and delete with this heap memory? new appears to me to be more 'type' friendly then malloc which seems to be pretty basic byte allocation orientated?
So my impression is that this added SRAM board is pretty easy and flexible to utilize, does that seem a fair assessment? I'm just not sure if trying to utilize all the possible ram via bank switching would work with new?
new / delete aren't implemented for the AVR GCC libaries. If you want new() you have to write it yourself. There are plenty of examples on the net, but ultimately it calls malloc().
If you want bank switching you're better off handling the memory yourself. A 16 bit pointer won't tell you which bank to access.
Do you have an application that needs more than 56k of RAM?