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

Reply
  • 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

Children
Related