3

I am working on an ARM Cortex-M4 processor using IAR Embedded Workbench. When the main stack overflows I get a bus fault. I have therefore written a small function in assembly to check if the bus fault was caused by a stack overflow, set the stack pointer and call a dedicated stack overflow handler.

The following code shows the function and it works fine. My problem is that I had to subtract 1 from the labels in the two LDR instructions and I don't understand why.

StackBegin:     DC32        SFB(CSTACK) ; Start of main stack
StackEnd:       DC32        SFE(CSTACK) ; End of main stack
BusFault_Handler:
                LDR         R0, StackBegin-1 ; No idea why we need to subtract 1
                CMP         SP, R0
                IT  GT
                BGT         BusFault_Post_Handler   ; Call if SP is OK
                LDR         SP, StackEnd-1          ; On stack overflow, set SP to top of main stack; No idea why we need to subtract 1
                B           MainStackOverflow_Handler

If I don't subtract 1, the LDR instructions load data one byte after the label. StackEnd contains the value 0x20000400, but SP is loaded with 0x5F200004 unless I subtract 1 from the label. The 0x5F is the first byte in BusFault_Handler.

Can anyone explain why I need to subtract 1. Have I configured something wrong. I have checked that the data is word (4 byte) aligned.

  • 2
    The two constants in program memory are probably not aligned correctly. Try adding `ALIGN 2` right before the two `DC32` instructions. Also, you need `IT HS` ("unsigned higher or same"), as pointers are unsigned, and the lowest stack address `SFB(CSTACK)` *is* a valid address. – Erlkoenig Aug 27 '19 at 09:35
  • Maybe `CSTACK` section is not aligned by 4? What asm code generated in both cases? _StackEnd contains the value 0x20000400_ how did you find out? – Stephen Plyaskin Aug 27 '19 at 09:39
  • 3
    It is possible that the assembler adds 1 to the label address because the label is in a text section in thumb mode. – fuz Aug 27 '19 at 10:08
  • 1
    @fuz - very good point. According to the [IAR manual](http://netstorage.iar.com/SuppDB/Public/UPDINFO/012120/arm/doc/EWARM_AssemblerReference.ENU.pdf#page=79): "Always use the DATA directive when defining data in a Thumb code section with DC8, DC16, or DC32, otherwise labels on the data will have bit 0 set.". An easier way to achieve the goal would be to do `LDR R0, =(SFB(CSTACK))` – Erlkoenig Aug 27 '19 at 11:06
  • You better clear bit 0 (`bic address, address, #1`) instead of subtracting by 1. – Jake 'Alquimista' LEE Aug 27 '19 at 23:35
  • @Jake'Alquimista'LEE It would be even better to have the assembler insert the correct address from the beginning. Doing it at runtime (bic) is even worse than the original assembly-time "-1". – Erlkoenig Aug 28 '19 at 04:22
  • @Erlkoenig - You are of course right that I need to use IT HS instead of IT GT. I should have looked it up. I also didn't know that I could use =(SFB(CSTACK)). That is a much better solution. Thanks. – Ivan Johansen Aug 28 '19 at 09:32
  • Note that you can use the `LTORG` directive to control where the literal will end up when using the `LDR r0, =(...)` syntax, which is basically just syntactic sugar for your original code. – Erlkoenig Aug 28 '19 at 09:38
  • subtracting one can/will bite you some day it is safer to use bic to clear the bit, if it was set then it clears it if it was not set then you dont get into trouble. Ideally addresses like this should not have been declared as functions and no action should have been needed to change it from a function address to a simple label. So this is a hack to undo something else. clean it up and you wont have these issues. – old_timer Aug 28 '19 at 13:33

1 Answers1

6

When the calculating the address for a label defined when in thumb-mode the assembler will automatically set the least significant bit since it assumes that code labels are branch-targets and nothing else. Thus, when the StackBegin and StackEnd labels are defined as part of the code, they will have the LSB set.

To avoid this problem make sure that the assembler is in data-mode when the StackBegin and StackEnd labels are defined.

        THUMB
BusFault_Handler:
        LDR         R0, StackBegin
        CMP         SP, R0
        IT  GT
        BGT         BusFault_Post_Handler   ; Call if SP is OK
        LDR         SP, StackEnd            ; On stack overflow, set SP to top of main stack;
        B           MainStackOverflow_Handler

        DATA
StackBegin:     DC32        SFB(CSTACK) ; Start of main stack
StackEnd:       DC32        SFE(CSTACK) ; End of main stack
        THUMB
Johan
  • 3,667
  • 6
  • 20
  • 25
  • Thanks a lot. I was not aware that LSB was set for labels in code sections. Putting it in a data section solves the problem. – Ivan Johansen Aug 28 '19 at 09:27