This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF51822 - Exception on Stack Overflow

Using:

  • nRF51822
  • GCC 4.8.4
  • Nordic Sdk 5.2.0
  • pure-gcc setup (Ole Morten)
  • newlib-nano and -flto

See also this question.

I'd defined some stack size within startup_nrf51.s and want to be informed if the stackpointer passes __StackLimit.

Might there be a way to generate something like a hardfault or something else? The stack overflow condition should be detected regardless if the device is in debugging environment or not.

Edit 2014-08-28

BTW: For the heap / malloc issue, I see a different approach using a custom _sbrk() implementation. I'll post it as soon as I got a solution.

Trying out the RLENR0 based approach as suggested by RK.

My ram memory layout is currently like this:

__data_start__ = 20002000
__data_end__   = 20002088
__bss_start__  = 20002090
__bss_end__    = 20002DC4
__HeapBase     = 20002DC8
__HeapLimit    = 200031C8
__StackLimit   = 20003400
__StackTop     = 20004000
__stack        = 20004000

Dump of current RLENR0 and CLENR0 values:

NRF_MPU->RLENR0  = 00002000
NRF_UICR->CLENR0 = 00014000
NRF_FICR->CLENR0 = FFFFFFFF

I'm using the Sdk 5.2 bootloader which performs a check to NRF_UICR->CLENR0

// This check ensures that the defined fields in the bootloader corresponds with actual setting in the nRF51 chip.
APP_ERROR_CHECK_BOOL(NRF_UICR->CLENR0 == CODE_REGION_1_START);

so I'm a bit afraid from changing CLENR0 register.

Now I stuck for finding the correct RLENR0 value and write that into NRF_MPU->RLENR0. I'd try it from within code (tried both startup asm and very start of main). By trying out some random values:

  • Info: At the time when changing NRF_MPU->RLENR0 within main, my stackpointer is at 0x20003EF0 (determined by calling __get_MSP()).
  • For values like 0x20003400, my application stops immediately. Don't know if my hardfault handler was called - if so, then he was unable to write something via uart.
  • For values above 0x2140, I earn a immediate hardfault however.
  • All values from 0x0000 up to 0x2140, my application runs like before.

For the last case, I see my changed value in NRF_MPU->RLENR0, but when calling softdevice_handler_init() the value will be restored to 0x2000. When I now try to change NRF_MPU->RLENR0 again, a hardfault is raised - regardless which value I try to set.

Could you help me to find the right value for NRF_MPU->RLENR0 and how to write it into?

  • If you were feeling particularly clever you could try getting your linker map to organise your code so it looks like this ...

    FLASH: [ Softdevice.. ][Your code ][ empty flash + pstorage ]

    RAM: [ Softdevice RAM ][ Thread Stack ][ ... empty ...][ Process Stack ][Heap]

    Then you set RLENR0 in the memory module to just before the Process Stack and make sure CLENR0 is set correctly too.

    Malloc - assuming you find out why yours appears not to be working properly, will then fail if you over allocate heap and return 0 and I believe that any code running in region 1 (above CLENR0, i.e. your code) will fail if it tries to write into R0 RAM, that should include pushing too much on the stack and descending it below RLENR0. According to section 8.1.2 of the manual, that should cause a hard fault. ( don't know why nothing seems to generate MemManage faults on this chip but there you go).

    That also nicely decouples your stack from the softdevice one.

    I keep meaning to set CrosssWorks up to do this, it has all the symbols in the thumb_crt0.s file to set up the second stack pointer but just sort of can't quite be bothered.

  • Is it possible to change RLENR0/CLENR0? Isn't softdevice changing this registers on start and block access to them (I can't found any softdevice call to change them and in S110_SoftDevice_Specification_v1.3A.pdf it's written that if softrevice is running access to MPU is restricted)?

  • Softdevice doesn't touch them, CLENR0 is written as part of the softdevice upload procedure (or not even at all in recent softdevices) and I've never seen anything write RLENR0 ever. I'd stick it in the hex file upload and do it there.

    This is partly why I haven't bothered.

  • I don't know how to do it with generating hardfault, but I just periodically check whether the stack overflowed or not and generate error if it is.

    For this I fill stack with 0xDEADBEEF in startup file and periodically check maximum stack usage.

    In reset handler:

    Reset_Handler:
        .fnstart
    
    /* Make sure ALL RAM banks are powered on */
        LDR     R0, =NRF_POWER_RAMON_ADDRESS
        LDR     R2, [R0]
        MOVS    R1, #NRF_POWER_RAMON_RAMxON_ONMODE_Msk
        ORRS    R2, R1
        STR     R2, [R0]
    
                     LDR     R0, =__StackTop
                     LDR     R1, =Stack_Size
                     LDR     R2, =0xDEADBEEF
    .Fill:
                     SUBS    R0,#4
                     STR     R2, [R0]
                     SUBS    R1,#4
                     BNE     .Fill
    

    Get maximum stack usage:

    extern uint32_t __StackTop;
    extern uint32_t __StackLimit;
    uint32_t stack_overflow_debug(void)
    {
        uint32_t stack_usage = 0;
        uint32_t offset = 0;
        uint32_t * value_addr = (uint32_t *) &__StackLimit;
    
        for (; offset <  ((uint32_t)&__StackTop - (uint32_t)&__StackLimit); offset=offset+4)
        {
            uint32_t new_val = *(value_addr + offset);
            if (new_val != 0xDEADBEEF )
            {
                break;
            }
        }
        stack_usage = ((uint32_t)&__StackTop - (uint32_t)&__StackLimit) - offset;
    
        return stack_usage;
    }
    
  • That's the way I currently also do it (and I also filling the heap with a different pattern). Actually, I'm far away from having stack usage problems. But If one occours, I think by producing a hardfault, there might be a way to identify a bit of callstack.

Related