Unable to read link register using __ASM("lr");

Additional details:

  • IDE: Keil uVision 5.17.00
    • C Compiler: V5.06 update 1 (build 61)
    • Assembler: V5.06 update 1 (build 61)
  • Custom board (This shouldn't make a difference)
  • SoftDevice: s140_nrf52_6.1.0
  • SDK: 15.2

Hello.

I am attempting to read the link register LR the same way that the PSP and MSP are read in all Nordic example projects. See __get_PSP() and __get_MSP() for reference. I have implemented the following to mimic __get_MSP():

__STATIC_INLINE uint32_t __get_LR(void)
{
  register uint32_t __regLR  __ASM("lr");
  return(__regLR);
}

void MemoryManagement_Handler(void)
{
   static volatile uint32_t lr;
   lr = __get_LR();
   
   if(lr & 0x4)
   {
      // Do something useful
   }
   else
   {
      // Do something useful
   }
}

// Trigger a MemManage fault from main;

main(void)
{
   typedef void (*fn_t)();
   fn_t func = (fn_t)((char*) 0xFFFFFFFF);
   func();
}

However, the link register value returned does not match what is shown in the Keil uVision registers window as seen below.

Note that I have no trouble reading the PSP or MSP. But for some reason I cannot read the link register,. Replacing __ASM("lr") with __ASM("r14") resulted in the same behavior. Oddly enough, when I hover my mouse over __ASM("lr"), it shows the correct value as seen in the register window. Lastly, looking at the HardFault_Handler() implementation in the Nordic examples as found in hardfault_handler_keil.c, it looks like the link register LR is checked using "tst lr, #4".

Any idea as to why I cannot read the link register using ASM("lr")? The same code works on a STM MCU in Keil uVision, but Nordic does not work.

Thanks!

Derek

Parents
  • I ran a test with the following parameters on a nRF52840 devkit (PCA10056) rev 2.0.2:

    • Project: C:\Nordic\nRF5_SDK_15.2.0_9412b96\examples\peripheral\blinky\pca10056\blank
    • Keil uVision 5.17.0
      • Optimization Level: 0
    • SES (Segger Embedded Studio) 5.10a
      • Optimization Level: None
    • main.c (Same exact code running in both IDE's)

    Here is the code for main.c (I can send you the project if need be):

    /**
     * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    /** @file
     *
     * @defgroup blinky_example_main main.c
     * @{
     * @ingroup blinky_example
     * @brief Blinky Example Application main file.
     *
     * This file contains the source code for a sample application to blink LEDs.
     *
     */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "nrf_delay.h"
    #include "boards.h"
    
    __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_LR(void)
    {
      register uint32_t __regLR  __ASM("lr");
      return(__regLR);
    }
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        /* Configure board. */
        bsp_board_init(BSP_INIT_LEDS);
        
       // Enable UsageFault_Handler, BusFault_Handler, MemoryManagement_Handler 
       SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk | SCB_SHCSR_MEMFAULTENA_Msk;
       
       // Trigger MemManage fault
       typedef void (*fn_t)();
       fn_t func = (fn_t)((char*) 0xFFFFFFFF);
       func();
        
        /* Toggle LEDs. */
        while (true)
        {
            for (int i = 0; i < LEDS_NUMBER; i++)
            {
                bsp_board_led_invert(i);
                nrf_delay_ms(500);
            }
        }
    }
    
    void MemoryManagement_Handler(void)
    {
       unsigned * dest = (unsigned *)0x2003DE00;
       static volatile unsigned lrVal;
       lrVal = __get_LR();
    
       static volatile unsigned * stkPtr;
       if (lrVal & 0x4)
       {
          stkPtr = (unsigned * )__get_PSP();
       }
       else
       {
          stkPtr = (unsigned * ) __get_MSP();
       }
       
       *dest++ = (unsigned) stkPtr;
       *dest = lrVal;
       while(1);
    }
    
    
    
    /**
     *@}
     **/
    

    Result:

    • Segger Embedded Studio works (The correct LR value is written to RAM)
    • Keul uVision does not work (Some garbage data is written to RAM for LR)

    Segger Embedded Studio (Works):

    Keil uVision (Does not work):

    I am not an assembly expert, but further investigation shows that for Keil, the __get_LR() function is being turned/optimized into a NOP:

    Segger Embedded Studio (Correct assembly):

    Keil uVision (Incorrect assembly):

    So it seems to me that maybe the Keil compiler for Nordic doesn't recognize __ASM("lr") and it is being optimized out? Hopefully you can explain why this may be happening?

    Thanks,

    Derek

  • Nordic has done nothing special to this compiler, it is the generic ARM compiler that is being used and seems like that instruction is being optimized out in Keil. You can probably try to use __return_address() instead of __get_LR() to see if that works. 

    Normally with ASM code and volatile being used in __get_LR() I would assume that nothing should have been optimized away here but seems like there is buggy behavior here on the ARm Keil compiler end.

  • You are a lifesaver. I didn't know that function existed. I was about to write an assembly function but that did the trick! I'll select your post as the answer once I integrate this fix into my actual project and unit test.

    Thanks!!

    Derek

Reply Children
Related