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

How does NRF_MESH_ASSERT work?

Hello, when reading the source code for a mesh node provisionee, I stumbled upon this MACRO function:

NRF_MESH_ASSERT

Edit: it is in ble_mesh_v0.9.1-Alpha\mesh\src\core\packet_mgr.c

it is defined as:

 #define NRF_MESH_ASSERT(cond)   if (!(cond))                        \
     {                                                                   \
         uint32_t pc;                                                    \
         GET_PC(pc);                                                     \
         if (m_assertion_handler)                                        \
         {                                                               \
             m_assertion_handler(pc);                                    \
         }                                                               \
         else                                                            \
         {                                                               \
             HARD_FAULT();                                               \
         }                                                               \
     }

There are quite a few lines of and within this function which confuse me due to their difficulties (and in any case, I'm not a huge fan of MACRO functions) but I'll just ask about 2 places which puzzles me the most:

#Question #1:

the

GET_PC(pc); 

is defined as:

#define GET_PC(__pc) do {                       \
        __pc = __current_pc();                  \
    } while (0)

Would I be correct in assuming that

__current_pc()

is some sort of compiler function or lower-level library function which returns the realtime/ during run-time PC counter value?

#Question #2:

What is the purpose of this function? To make sure last_block_size is actually greater than 0?

Please provide help, thank you!

  • Hi Mitch,

    Q1: Yes, the purpose of the __current_pc() function is to return the program counter at the time of the assert.

    Q2: I assume that you mean the NRF_MESH_ASSERT macro, it used to verify that the provided condition is true, if not then the program execution can not continue, e.g.

    NRF_MESH_ASSERT(p_evt != NULL);
    

    where the macro is used to verify that the p_evt pointer is not a NULL pointer. If it is a null pointer then the program should continue its execution and we end up in the assertion handler.

    Wikipedia provides a pretty decent explanation:

    An Assertion is a statement that a predicate (Boolean-valued function, a true–false expression)is expected to always be true at that point in the code. If an assertion evaluates to false at run time, an assertion failure results, which typically causes the program to crash, or to throw an assertion exception.

    Best regards

    Bjørn

  • Hi Mitch996,

    Edit: I saw Bjørn beat me to it!

    You're right that these macro functions aren't really user friendly :) The use of macro functions is often needed unfortunately when trying to support multiple platforms and compilers at the same time in addition to perform very low-level operations.

    Question 1

    The GET_PC() macro is simply for getting the current program counter. The program counter is a CPU register and cannot be directly accessed from C, therefore we need to use the __current_pc() intrinsic provided by ARMCC. With GCC we use some inline assembly to do the same job:

    #define GET_PC(__pc) do {                       \
            __asm volatile (                        \
                "MOV %0, pc\n\t"                    \
                : "=r" (__pc)                       \
                );                                  \
        } while (0)
    

    Question 2

    The purpose of the NRF_MESH_ASSERT() macro is essentially a way of letting the application know that we've encountered an irrecoverable error condition. It is used in, e.g., the Design by Contract methodology. Where we assert if an assumed condition doesn't hold. This is very helpful when implementing robust modules that should "always work".

    One case is in buffer_remove_from_free_list() in packet_mgr.c where the statement

    static void buffer_remove_from_free_list(buffer_header_t * buf)
    {
        buffer_header_t * p_cur = mp_free_head;
        /* If the buffer to be released is a head, then move the head and return */
        if (p_cur == buf)
        {
            mp_free_head = p_cur->p_next_free;
            return;
         }
    
        /* Find the buffer in the free list */
        p_cur = buffer_find_preceding_free_block(buf);
        NRF_MESH_ASSERT(p_cur != NULL);
    
        /* Remove the buffer from the free list */
        p_cur->p_next_free = buf->p_next_free;
    }
    

    tests that a buffer that has been removed from the free list has a parent. This should always hold since we test that the buffer to be removed isn't the linked list head in the precedingif-statement. Thus, if this assertion fires, there is something very wrong with our free list.

    Hope this helps,
    Thomas

Related