The subroutine(C source code) is the same, but when compiled:
The DEMO's disassembly:
PUSH {r4-r7} ... ... ... POP {r4-r7} BX lr
My project's disassembly:
PUSH {r4, lr} ... ... ... POP {r4, pc}
The situation is I clean all the RAM during the subroutine to 0. So after "POP {r4, pc}", the program is crashed. I don't konw assembly language very well. So what could I do to make the ARMCC generate disassembly code using "BX lr" to make my code work? I'm using STM32F103/MDK5/ARMCC5.06.
Make sure your function doesn't call any other functions so it doesn't need to push LR
Thanks for your replay. think my description is not clear.
The key point is not that "PUSH LR or not". This is a RAM startup selftest subroutine, so it does cleanup the whole RAM, including STACK&HEAP. When return from subroutine, the demo use "bx lr", it worked of course. but my project using "pop {r4, pc}" pops from stack make PC = 0x00000000, so it crashed.
my question is how could I do to make my source code generate disassembly using "bx lr" to return from the subroutine correctly? Change some MDK options? Make some change in the .sct file? Change the optimization level? etc.
Appreciate your help anyway.
Yes, and my point is that it doesn't PUSH LR unless you call some other function(s), otherwise it has to put it somewhere or it will get overwritten when you branch to the other subroutines.
May be you should test the RAM earlier, and write the test in assembler where you can control what registers are used and what it calls?
Based on your knowledge of the core/assembler, why do you think it pushes LR? Why don't you look at all the code it is emitting for the function?
It is not a matter of settings, or options, it is about what you are DOING in the function.
I wouldn't expect this function to PUSH LR, or any registers
int foo(void) { return(12345); }
It's possible to create a RAM-test that is non-destructive by saving the RAM contents before testing to write. And then restore the original content.
If the RAM is broken, then the above will fail. But that's irrelevant since with broken RAM you just have to settle for setting an error LED and then busy-loop or similar until power is cut.
But as Pier notes, it's when your function contains further function calls that the compiler needs that additional push since a fixed register can only store a single copy of the LR value.
Thanks for your replay (Westonsupermare Pier & Per Westermark)
The whole process is: Power_on --> Reset_handler -> Selftest -> main
1. The Reset handler is assembler file. 2. The selftest is C file, including CPU/FLASH/RAM selftest which is called from separate files. 3. The RAM selftest file is C file which is just 6 <for loops> inside, and do not call any other subroutine.
1. When call RAM selftest subroutine, the compiler generate "BL.W" to write the return address into LR which it's supposed to do and it's right. 2. It doesn't make any sense that the compiler generate "PUSH LR" stuff for me. Because I don't call any subroutines in the RAM selftest. 2. After the RAM test is done, it's supposed to generate "BX LR"(in the demo) other than "POP R4, PC" to return.
If I can't make something outside the source code to make the ARMCC generate "BX LR" for me, I'll have to do it manually using assembler.
Thanks for your help again. And I'm trying rewrite the RAM selftest file using assembly language. Hope it words.
Review a disassembly of the function. It is more than likely you use math or other functionality that calls into library code.
Keil can generate listing files, or you can use the included FromELF utility.
If your optimization level is 0 it often (probably even always) will push and pop the lr "just because" even with no function calls within to called function. I would be careful with any assumptions about what the compiler must do. I would still want my code to work at optimization level 0 so I would just write this in assembly language to be safe. (you can examine the C to assembly listing to get a good idea of how the assembly language should be structured). I would execute it before __main is called.
I think ARM's got some 30+ years of experience building compilers/assemblers, and knowing if a subroutine has to branch to another, or not, and where it is going to stuff the literals, place near branches, etc. Optimization enabled, or not.
Keil, with optimization disabled...
i.foo foo 0x08000922: f2430039 C.9. MOV r0,#0x3039 0x08000926: 4770 pG BX lr
My assumptions are based on some pretty long standing appreciation of what comes out of assorted tools, and the "trust but verify" approach to confirming them when needed.
The OP is building a function that calls other code, either known or unknown to him. A full disassembly of the routines in question would quickly dispel any doubt, but we argue back-and-forth based on a handful of prologue/epilogue code instructions about what's going on inside. So instead of a solution in minutes it goes for days.
I don't think you can stop the compiler outputting the push/pop of LR/PC unless you force it to in-line everything it might use or call otherwise, and I think that is unduly hopeful.
In things where testing RAM is supposed critical, you'd hope the people implementing the critical code would have a better appreciation for what's actually critical.
Thanks for your reply. (Robert McNamara, Westonsupermare Pier)
@Robert McNamara Yes, my optimization level is 0 and these whole selftests(cpu/flash/ram/excute process) is before __main.
@Westonsupermare Pier It's just 6 loops inside like below:
/* ---------------------------- STEP 3 ----------------------------------- */ /* Verify inverted background and write background with addresses increasing */ for (p = RAM_START; p <= RAM_END; p += RAM_BLOCKSIZE) { for (j = 0u; j < RAM_BLOCKSIZE; j++) { if ( *(p + (uint32_t)RAM_SCRMBL[j]) != INV_BCKGRND) { Result = ERROR; } *(p + (uint32_t)RAM_SCRMBL[j]) = BCKGRND; } }
I'm not familiar with the compilers/assemblers as well as ARM/Keil experts, so I couldn't find where to change to influence the complier/assembler's behavior.
I won't digging this any more and the solution is I rewrite the RAM_selftest using assembly language. And it works, for now. About the 'Critical' stuff is actually iec60335 related which I don't understand very much but have to follow that. Thanks for all your help again.