6

I'm currently working on a bootloader for an ARM Cortex M3.

I have two functions, one in C and one in assembly but when I attempt to call the assembly function my program hangs and generates some sort of fault.

The functions are as follows,

C:

extern void asmJump(void* Address) __attribute__((noreturn));

void load(void* Address)
{
    asmJump(Address);
}

Assembly:

.section .text

.global asmJump

asmJump:                   @ Accepts the address of the Vector Table
                           @ as its first parameter (passed in r0)

    ldr r2, [r0]           @ Move the stack pointer addr. to a temp register.
    ldr r3, [r0, #4]       @ Move the reset vector addr. to a temp register.

    mov sp, r2             @ Set the stack pointer

    bx  r3                 @ Jump to the reset vector

And my problem is this:

The code prints "Hello" over serial and then calls load. The code that is loaded prints "Good Bye" and then resets the chip.

If I slowly step through the part where load calls asmJump everything works perfectly. However, when I let the code run my code experiences a 'memory fault'. I know that it is a memory fault because it causes a Hard Fault in some way (the Hard Fault handler's infinite while loop is executing when I pause after 4 or 5 seconds).

Has anyone experienced this issue before? If so, can you please let me know how to resolve it?

As you can see, I've tried to use the function attributes to fix the issue but have not managed to arrive at a solution yet. I'm hoping that someone can help me understand what the problem is in the first place.

Edit:

Thanks @JoeHass for your answer, and @MartinRosenau for your comment, I've since went on to find this SO answer that had a very thorough explanation of why I needed this label. It is a very long read but worth it.

artless noise
  • 21,212
  • 6
  • 68
  • 105
nonsensickle
  • 4,438
  • 2
  • 34
  • 61
  • 1
    If you have different interrupt routines for bootloader and application: Have you disabled interrupts and replaced VTOR (vector table offset register) with the vector table of the second program? This should be done before setting the stack pointer. – user694733 Sep 17 '13 at 08:01
  • 2
    1) Are you sure the stack pointer is correct? 2) If your code is THUMB code then r3 must not be the address of the code but the address of the code plus 1! – Martin Rosenau Sep 17 '13 at 08:21
  • 1
    Try flushing the *write buffer*, *dcache* and *icache* before your call. Did you copy code somewhere? It sounds like a *code* `!=` *data* issue or pipeline effect; which explains it working when paused in the debugger. – artless noise Sep 17 '13 at 17:08
  • @user694733 I have not disabled the interrupts but you're right that I should. Still, once an interrupt is serviced it should return back to the code it was executing before, i.e. my reset vector. – nonsensickle Sep 17 '13 at 22:07
  • @MartinRosenau 1) The stack pointer is retrieved from the Vector Table which is generated by the linker so I'm confident that it is correct. 2) That is exactly what the problem was and Joe Hass' answer helped me realize that. – nonsensickle Sep 17 '13 at 22:10
  • @artlessnoise I did try using the `dsb`, `isb` instructions but they were not the problem. It was a far sillier mistake. I simply did not know that my assembly was not treated as thumb code without the `.thumb_func` flag by the linker. – nonsensickle Sep 17 '13 at 22:11

3 Answers3

8

I think you need to tell the assembler to use the unified syntax and explicitly declare your function to be a thumb function. The GNU assembler has directives for that:

  .syntax unified
  .section .text
  .thumb_func
  .global asmJump
asmJump:

The .syntax unified directive tells the assembler that you are using the modern syntax for assembly code. I think this is an unfortunate relic of some legacy syntax.

The .thumb_func directive tells the assembler that this function will be executed in thumb mode, so the value that is used for the symbol asmJump has its LSB set to one. When a Cortex-M executes a branch it checks the LSB of the target address to see if it is a one. If it is, then the target code is executed in thumb mode. Since that is the only mode supported by the Cortex-M, it will fault if the LSB of the target address is a zero.

  • That worked! I'm going to search for why that worked, but if you're feeling generous and expand your answer it would be help me a lot. Regardless, you solved my problem, thanx! – nonsensickle Sep 17 '13 at 21:03
  • 1
    Thanks again. I have found an explanation for this in another SO question here: http://stackoverflow.com/questions/9368360/arm-thumb-using-bx-in-thumb-code-to-call-a-thumb-function-or-to-jump-to-a-thu – nonsensickle Sep 17 '13 at 21:26
  • I have tried running the code without the `.syntax unified` but it seems to fail. I'm not sure why this would be the case. Could you please include an explanation in your answer if you have one? – nonsensickle Sep 17 '13 at 22:15
  • 1
    For those coming from x86: ARM switches Thumb instruction encoding on / off at the same time that it runs a `blx` (`call`) instruction. That is why `.thumb_funct` is a per-function thing. – Ciro Santilli OurBigBook.com Jun 23 '16 at 22:09
1

Since you mention you have the debugger working, use it!

Look at the fault status registers to determine the fault source. Maybe it's not asmJump crashing but the code you're invoking.

Community
  • 1
  • 1
Igor Skochinsky
  • 24,629
  • 2
  • 72
  • 109
0

If that is your all your code.. I suppose your change of SP called the segment error or something like that. You should save your SP before changing it and restore it after the use of it.

ldr r6, =registerbackup
str sp, [r6]
#your code
...
ldr r6, =registerbackup
ldr sp, [r6]
TwoCode
  • 127
  • 7
  • You would normally be right. But since this is a bootloader I'm intentionally setting the stack pointer without a backup option. The new stack pointer address is what I have read from the Vector Table of the code which I am trying to load. Sorry if I hadn't made that clearer. – nonsensickle Sep 17 '13 at 20:44
  • So the problem was an arm calling a thumb function. There you go. Great u found the root cause. – TwoCode Sep 17 '13 at 22:52