Reply by Anton Erasmus January 17, 20052005-01-17
On 14 Jan 2005 at 19:15, sig5534 wrote:
>
> 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:
>
> void __attribute__((section(".data"))) IAP_Test( void ) {
> U32 N;
> N=IOPIN;
> } // IAP_Test
>
> 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.
>
>


The following seems to work:



Both function foo and global int global1 is put into the data section.

Regards
Anton Erasmus

An Engineer's Guide to the LPC2100 Series

Reply by sig5534 January 17, 20052005-01-17
YEH! I think I got it working.

The problem was in my startup.s file. I had turned off a flag before
that left my .data section init routine bypassed. So the data
section was not being init'd and therefore the pointer at 0x40000000
and the code itself at 0x40000004 were actually zeros/junk.

When I turned the .data init back on, the call is working! The
function is in RAM, the ptr is good, and it is running.

So to summarize, to get and use a function in RAM you only need to do
three things:

(1) Have an init in the startup code for the .data section.
(2) Use the __attribute__((section(".data"))) on the functions.
(3) Define function pointer variables for the functions and make
indirect calls using those.

That seems to do it. I'm sure there are other ways too.

Thanks guys. Now off to get my IAP code done.

Chris.
Reply by Charles Manning January 16, 20052005-01-16
More below...

On Monday 17 January 2005 13:34, sig5534 wrote:
> Hold the phone, something funny is going on here. The debugger says
> that IAP_Test() is at adr 0x40000000. However when I look at the
> dissassembly I see this:
>
> pramfunc:
> 0x40000000 E321F0D1 DD 0xE321F0D1 ; DATA SECTION
> 14: return IOPIN|n;

pramfunc is supposed to be a pointer, so it should be holding 0x40000004 or
something (ie. a pointer to the actual function).

Try consting the pointer... this should shift it into the literal pool. This
will alter the code slightly and maybe make it work.

> IAP_Test:
> 0x40000004 E59FD05C LDR SP,[PC,#+0x5c]
> 0x40000008 E321F0D2 MSR CPSR_c,#0xd2
> 0x4000000C E59FD054 LDR SP,[PC,#+0x54]
> 15: }
> 0x40000010 E321F0D7 MSR CPSR_c,#0xd7
> 0x40000014 E59FD04C LDR SP,[PC,#+0x4c]
>
> This looks like there is some kind of data or segment marker right at
> 0x40000000. However, the actual code is at 0x40000004.
>
> So it appears the adr being loaded into the PC is 0x40000000 and that
> does not appear to be code.
>
> What's going on here? Why did the compiler/linker do this?

What seems strange to me is that the code you showed was

LDR PC,[R5,#0]

This is not what I'd expect unless the previous instruction was

MOV LR,PC

This would syntesize a branch and link.

I'd expect more to see something like:

MOV or LDR to get address into a register Rx

BL Rx

What version compiler/linker are you using? Could be an older broken one.

>
> It is placing this RAM function before the variables. The RAM
> variables have been moved to 0x40000020.
>
> I'd rather have it the other way around. The RAM functions after the
> variables. That might solve this problem too.
>
> Any thoughts on how to get the RAM functions after the RAM
> variables? Do I need to define some more segments and the the linker
> how to order them?

To control the placement you really need to use an ldscript and if you're
doing that you might as well then use the ldscript to do the placement for
you.

I do this with a section that looks like:




This makes a whole seperate section that I copy in maincrt.s, just like the
.data section is copied.

You could also place this at the end of the .data section and see if that
works.
Reply by sig5534 January 16, 20052005-01-16
Hold the phone, something funny is going on here. The debugger says
that IAP_Test() is at adr 0x40000000. However when I look at the
dissassembly I see this:

pramfunc:
0x40000000 E321F0D1 DD 0xE321F0D1 ; DATA SECTION
14: return IOPIN|n;
IAP_Test:
0x40000004 E59FD05C LDR SP,[PC,#+0x5c]
0x40000008 E321F0D2 MSR CPSR_c,#0xd2
0x4000000C E59FD054 LDR SP,[PC,#+0x54]
15: }
0x40000010 E321F0D7 MSR CPSR_c,#0xd7
0x40000014 E59FD04C LDR SP,[PC,#+0x4c]

This looks like there is some kind of data or segment marker right at
0x40000000. However, the actual code is at 0x40000004.

So it appears the adr being loaded into the PC is 0x40000000 and that
does not appear to be code.

What's going on here? Why did the compiler/linker do this?

It is placing this RAM function before the variables. The RAM
variables have been moved to 0x40000020.

I'd rather have it the other way around. The RAM functions after the
variables. That might solve this problem too.

Any thoughts on how to get the RAM functions after the RAM
variables? Do I need to define some more segments and the the linker
how to order them?

Chris.
Reply by Richard January 16, 20052005-01-16
C syntax portion is easy:

void (*func)(void) = (void (*)())0x40000000;

(*func)();
Reply by sig5534 January 16, 20052005-01-16
> 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.
Reply by embeddedjanitor January 16, 20052005-01-16
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.
Reply by Richard January 16, 20052005-01-16
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,
Reply by sig5534 January 16, 20052005-01-16
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.
Reply by ntfreak2000 January 15, 20052005-01-15
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