4

I am having trouble understanding my compiler. We use the Scons utility to compile ARM code for an M0+ processor (on Windows, if it matters. It's an ARMCC compile) I am trying to capture the address of the stack pointer during an interrupt using the answer to this question.

For my compiler, I get the following: Error: #20: identifier "asm" is undefined The only way that I was able to get it to (mostly) compile was with the following:

  void IRQHandler_real( uint32_t *sp )
  {
     // Do some work
  }

  __asm void IRQHandler( void )
  {
     MOV R0,SP
     ADDS R0, #1
     LDR R1, =IRQHandler_real //<<-- This line fails
     BX R1
  }

The error code is

  Error: A1516E: Bad symbol 'IRQHandler_real', not defined or external

Replacing the last two assembly lines with BL IRQHandler_real results in the same error.

This answer explains that inline assembly is extremely compiler dependent, so I haven't been able to find an example that matches my code.

I read here that I need an "extern C" call, but I haven't yet found details on what that looks like.

I double checked my syntax of the compiled processor code, and it looks identical to the commands that I am using. I think that the symbol is not defined at the time when the assembly is evaluated. I just haven't figured out how to define and link the symbols.

TL:DR, I need to call a C function from ARM assembly, but I haven't gotten past the compiler.

artless noise
  • 21,212
  • 6
  • 68
  • 105
MPStoering
  • 184
  • 2
  • 12
  • Try adding an underscore to the beginning of `IRQHandler_real`: `_IRQHandler_real` – Thomas Jager Jun 26 '18 at 16:29
  • I think the code is also intended to pass SP to the called function rather than PC. – supercat Jun 26 '18 at 16:31
  • Thomas, I tried both single and double underscores, both failed with the same compile. – MPStoering Jun 26 '18 at 16:37
  • Supercat, I updated the question. Thanks for the catch. – MPStoering Jun 26 '18 at 16:37
  • I'm not clear why you're adding 1 to R0, but I'd suggest checking whether the assembler can handle the `=` syntax when used in this context by trying `ldr r0,=123456789`. If you can use a RAM-based vector table, another possibility which would avoid the need for compiler-dependent code would be to define a structure `struct { uint64_t code; void (*func)(uint32_t *sp);} intBoing`, store 0xBF00470849014668 into `code`, and the address of the function into `func`. Then store the address of `intBoing`, plus 1, into the desired interrupt vector. If I figured it right, btw, the code is: – supercat Jun 26 '18 at 16:52
  • `mov r0,sp / ldr r1, [pc+4] / bx r1 / nop`. – supercat Jun 26 '18 at 16:53
  • I feed that code into gcc and I get errors, fundamental errors with the asm directive, much less how it is dealing with labels. So maybe you are using C++ despite the C tag on the question, or maybe you are not using gcc at all? – old_timer Jun 26 '18 at 17:44
  • if the bottom line is you need to call C from assembly why not just use real assembly rather than inline, your loading of the address or a bl should both work and the bl should have the range on an cortex-m to reach most if not all of your flash so should just work. – old_timer Jun 26 '18 at 17:45
  • the adding one to the stack pointer doesnt make sense but maybe that is something specific to your interrupt handling... – old_timer Jun 26 '18 at 17:47
  • 1
    All of our code is written in C. I asked around the office, it looks like we are using ARMCC 5.06, not GCC ARM. I was trying to avoid creating a new file for such a small change, but I can move it into a new file if there's no inline solution. I'll try loading the struct into the interrupt vector and will check back with that – MPStoering Jun 26 '18 at 18:06

2 Answers2

2

If your compiler is a C++ compiler you will need to use extern "C" like this;

extern "C" {
  void IRQHandler_real( uint32_t *sp )
  {
     // Do some work
  }
}
cleblanc
  • 3,678
  • 1
  • 13
  • 16
  • I would expect that this would work for VS/C++. Unfortunately, it doesn't work for ARMCC. – MPStoering Jun 26 '18 at 21:37
  • 1
    @MPStoering you might need to surround it by `#ifdef __cplusplus` but it's documented [here](https://developer.arm.com/products/software-development-tools/compilers/arm-compiler-5/docs/dui0471/f/mixing-c-c-and-assembly-language/including-your-own-c-header-files-from-c) on the ARM website. I didn't think anyone uses armcc anymore, but I'm also sure this works with gcc. – cleblanc Jun 26 '18 at 21:46
1

It seems like the compiler is evaluating the ARM code separately from and after all C code is evaluated. I fixed it by importing the function with the IMPORT command. My final code looks like this:

IMPORT IRQHandler_real
PUSH { R4, lr }
MOV R4, SP; The compiler does this from the C code, I'm assuming to avoid a hardware limitation.
MOV R0, R4
BL IRQHandler_real
POP { R4, PC }
ENDP

This works for ARMCC 5.6. I think that most of the documentation out there is for GCC ARM (asm("statement");). I am still getting Warning: #667-D: "asm" function is nonstandard, so I will gladly accept a "more correct" answer if one exists.

MPStoering
  • 184
  • 2
  • 12