EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

How to declare RAM functions in GCC

Started by sig5534 January 13, 2005
In the IAR compiler there was an attribute to declare a function to
get it mapped into RAM. How is this handled in GCC? Is there some
equiv attribute?

Thanks, Chris.

An Engineer's Guide to the LPC2100 Series

Chris,

One method is to compile as normal in gcc then use objcopy to rename
the .text segment to .data.
gcc ... file.c
objcopy --rename-section .text=.data file.o

Make sure your startup file inits the data section and away you go.

The other method is to add the file to the .data section in your
linker script, eg.



This can sometimes fail if you have a *(.text*) in your text section.

You may sometimes need to use -mlong-calls gcc option or #pragma
long_calls depending the device memory.

It could probably be done with __attribute__ ((section (".."))) - not
tried this method.

Hope this helps
sjo
Thanks sjo. Exactly what I was looking for.

I'm trying to get your last method to would, using the attribute.
This is what I did for a test function:



When I make I get:

main.o(.text+0x150): In function `main':
/cygdrive/d/GNUARM/Projects/Vader/main.c:96: relocation truncated to
fit: R_ARM_PC24 IAP_Test
collect2: ld returned 1 exit status
D:\GNUARM\bin\make: *** [vader.elf] Error 1

The line #96 in main is where I put the call to IAP_Test(). GCC is
taking the attribute, and seems to be doing something with it, but it
is giving me this relocation error. I don't know what R_ARM_PC24 is.

Do I need to allocate extra space for this in my startup.s file?

Can you give any help on this? This would be great to get working
with this method.

Thanks, Chris.
Nice trick of just locating the RAM function in .data.

With main() in flash and IAP_Test() in RAM, they are ~1GB apart, and
a normal BL instruction can only handle distances of up to +/- 32 MB.
Try calling it indirectly:

static void (*pramfunc)(void) = IAP_Test;
(*pramfunc)();

Karl Olsen
> Try calling it indirectly:
>
> static void (*pramfunc)(void) = IAP_Test;
> (*pramfunc)();
>
> Karl Olsen

Karl, you're close but no cigar yet. I tried it, and the compiler
took it, but when the call gets made I get a PreFetch Abort Excep
error.

The IAP_Test() func got put in RAM correctly. It is right at
0x40000000. But the long call methodology is not quite right.
The "sjo" person mentioned something about another long call method.
But I'm not quite sure how he meant to set that up with the pragmas.

It's almost there. Anymore ideas on making the long call?

Thanks, Chris.
Either use gcc option -mlong-calls on the src file or
#pragma long_calls
void myfunc(void);
#pragma no_long_calls
in the header file

The objcopy is the simplest and will cause the least problems,
especially if you have a *(.text*) defind in your linker script.

Regards
sjo
Same result. Still not working. I get the Prefetch Abort Except.

When I trace it in the debugger I can see that it gets right to the
call instruc:

LDR PC,[R5,#0]

And then it jumps to the Prefetch Abort Excep.

R5 is loaded with 0x40000000 which is the right RAM area, and the
debugger says the IAP_Test() routine is at that location. I can see
the code is there as well in the debugger source.

Everything is right but the CPU is complaining about it's pipeline.
Obviously executing code in flash ROM and then jumping to RAM is
entirely different types of memory. It's confusing the memory
manager prefetch pipeline.

There must be something I am missing. Something else is apparently
necessary to tell the CPU the next instruc is in RAM. Otherwise the
CPU instruc fetch fails. Why?

Chris.
Looks like you found the cause. You just need to tell the CPU to flush its
pipeline. In the ARM ARM book, it mentions that

"The overall result is that code which writes one or more instructions to
memory and then executes them (know as self modifying code) cannot be
executed reliably on ARM processors without special precautions..." and then

"Each implementation therefore defines a sequence of operations that can be
used in the middle of a self-modifying code sequence to make it execute
reliably. This sequence is called an Instruction Memory Barrier...."

So "all" you have to do is look thru the LPC2K datasheet and hope they
describe what the IMB sequences are!!

Good luck,
From what I've seen, the self modifying code precautions in the ARMARM
are mostly relevant to CPUs with caches. With caches, particularly
with CPUs that have seperate caches for instructions and data. Clearly
if you have an instruction cache that is different from the data cache
you need to flush the caches to get the instructions to fetch
properly.

The LPC21xx does not have caches, except for the MAM, so this is not
an Ichache/Dcache issue.

I regularly use RAM-based code on ARM7s, but I achieve this by using
the linker (GNU ld) to set up the addresses and my maincrt.s copies
the RAM code from flash to RAM at start up. This is essentially what
the .data thing will do too.

Some things that are worth considering:
1) Try turning off the MAM to eliminate this from the set of variable.

2) Are you using Thumb code? If so, perhaps the .data assignment is
throwing away the thumb attributes.

3) A Prefetch Abort means that there is no memory available at the
address you specify (like a data abort, but for instructions).

The code

LDR PC,[R5,#0]

should be loading the pointer from this address (ie. R5 + #0 should be
a 32-bit value that is the pointer to the code).

Check that the address actually contains the **pointer** to the code
you want to execute and not the actual code itself.

Perhaps try

static const (*fnptr)(void) = IAP_Test;
...
*fnptr();
...

which should put the pointer in the literal pool and generate
different code.
> 1) Try turning off the MAM to eliminate
> this from the set of variable.

Already using MAM_OFF.

> 2) Are you using Thumb code? If so, perhaps the .data assignment is
> throwing away the thumb attributes.

Nope, 32b code.

> 3) A Prefetch Abort means that there is no memory available at the
> address you specify (like a data abort, but for instructions).
>
> The code
>
> LDR PC,[R5,#0]
>
> should be loading the pointer from this address
> (ie. R5 + #0 should be a 32-bit value that is the pointer
> to the code).
>
> Check that the address actually contains the **pointer**
> to the code you want to execute and not the actual code itself.

Now we're getting somewhere. I think this is the problem. I want it
to jump to and execute at the 0x4000000 adr. There is no pointer
there.

> Perhaps try
>
> static const (*fnptr)(void) = IAP_Test;
> ...
> *fnptr();

That gives a compiler error:

main.c:107: error: invalid type argument of `unary *'
D:\GNUARM\bin\make: *** [main.o] Error 1

> which should put the pointer in the literal pool and generate
> different code.

Yeh I think this is on the right track. I need to get my C syntax
correct here to generate a long jump directly rather than loading a
pointer. The function is at 0x40000000 not a pointer to it.

If you have any other ideas for this, please pass them on. Meanwhile
I check my C books to try to figure out the needed syntax.

Chris.

The 2024 Embedded Online Conference