I'm trying to execute a function from RAM on a Cortex-M3 processor (STM32). The function erases the and rewrites the internal flash, so i definitely needs to be in RAM but how do I do that?
What I hav开发者_如何学运维e tried is this: Copy the function to a byte array in RAM using memcpy (checking that it gets aligned correctly), setting a function pointer to point to the byte array an then calling the the function(pointer).
This works fine for maybe 10 instructions (I can follow the execution with the debugger) but then I get a buss error and the processor resets. The buss error occurs on the second pass through a loop so the code should be fine (as it works the first pass). I'm thinking that the faster RAM access mucks up the buss timing in some way...
Anyway is there a correct way to do this? How would a scatter file look like that places a function in RAM automatically (I'm using Keil uVision for Cortex-M3)?
Edit: More info: Toolchain: RealView MDK-ARM V 4.10 Compiler: Armcc v4.0.0.728 Assembler: Armasm v4.0.0.728 Linker: ArmLink v4.0.0.728 Processor: STM32F103ZE
The IMPRECISERR bit is set in the buss fault register when the reset happens.
The crash upon loop iteration is probably because the function is branching to an absolute address and is not relative to the new function location in RAM. Would accessing the original code location at that point cause a bus error because of the flash erase operation?
I believe you can mark a function to be compiled and copied to RAM correctly with CARM by appending the __ram
directive to the function definition. For instruction on how to do the same with the RealView compiler see the EXECUTING FUNCTIONS IN RAM technical support article:
µVision allows you to locate modules to specific memory areas that are entered in the dialog Project - Options - Target. To do so, right click on a source file (or file group) and open the dialog Options - Properties. Then select the memory regions under Memory Assignment.
There is an example in the folder ARMExamplesRAM_Function.
That should generate startup code to take care of copying the function to RAM and linking calls to that location correctly. Otherwise, if you need to dynamically copy arbitrary functions to RAM, then look into compiling position independent code (PIC) with RealView.
Without knowing more about your situation I can only suggest a few general things... make sure you have a valid stack for that function (or avoid all stack operations in the function), that your interrupts are disabled, and that any vectors in the system vector table don't point to code that goes away when you erase flash. Lastly, make sure your function is linked to run at the address you put it... the code may not be relocatable and may jump to a spot in is old location.
Because the ARM has a limited ability to load immediate data, utilities which generate code for the ARM frequently juxtapose code and data. For example, a statement like
void myRoutine(void)
{
myVar1=0x12345678;
myVar2=0x87654321;
}
might end up as something like:
myRoutine:
ldr r0,=myVar1; Load the address of _myVar
ldr r1,=0x12345678
str r1,[r0]
ldr r0,=myVar1; Load the address of _myVar
ldr r1,=0x87654321
str r1,[r0]
bx lr
which would get translated into:
ldr r0,dat1
ldr r1,dat2
str r1,[r0]
ldr r0,dat3
ldr r1,dat4
str r1,[r0]
bx lr
... followed some time later by
dat1 dcd _myVar1
dat2 dcd 0x12345678
dat3 dcd _myVar2
dat4 dcd 0x12345678
or perhaps even something like:
mar r0,dat1
ldrm r0,[r1,r2,r3,r4]
str r2,[r1]
str r4,[r3]
bx lr
... followed some time later by
dat1 dcd _myVar1
dat2 dcd 0x12345678
dat3 dcd _myVar2
dat4 dcd 0x12345678
Note that _myVar and 0x12345678 may be placed immediately following the code for the routine in which they appear; if you try to determine the length of the routine using a label which follows the last instruction, such length will fail to include the supplemental data.
An additional thing to note with the ARM is that for historical reasons, code addresses will often have their least significant bit set even though code actually starts on half-word boundaries. Thus, an instruction whose address is 0x12345679 will occupy either two or four bytes starting at 0x12345678. This can complicate address calculation for things like memcpy.
My recommendation would be to write a small routine in assembly language to do what you need. It's only a few instructions, you can know exactly what the code is doing and what address dependencies it might have, and you won't have to worry about future compiler versions changing your code in such a fashion as to break something [e.g. the third version of the above code would have no problem even if dat1
landed on an odd halfword boundary since the M3's LDR instruction can handle unaligned reads, but the fourth (slightly faster and more compact) version using LDRM would fail in such a case; even if today's version of a compiler uses four LDR instructions, a future version might use LDRM].
With the IAR compiler (I know your question is about Keil but I don't have it to play with) you can mark either the whole project or an individual file to be "position independent". From using this in the past with other processors it means you can move it "anywhere" and it will still work ok
精彩评论