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

Setting a watchpoint dynamically

Hello,

I am trying to spot a bug in which some memory corruption is happening. I don't know in advance at which address the corruption will happen, as it happens in some circular buffer. Maybe something is wrongly reading an address referring to this buffer and then writing there. I don't know.

Anyhow, I have found some way to store in some global variable mem_corruption_address the  data memory address at which the corruption before it happens, just in case it would happen, and what I want to do it call some watch_corruption_enable function at some point before code section in which the corruption may happen and watch_corruption_disable after this code section.

So here is the code which I have written : 

extern uint32_t const volatile* volatile mem_corruption_address;
uint32_t const volatile* volatile mem_corruption_address = NULL;

nrf_atomic_flag_t watch_corruption;

static void watch_corruption_set_watchpoint(void)
{
    if (watch_corruption) {
        DWT->COMP0     = (uintptr_t)mem_corruption_address;
        DWT->MASK0     = 0; // range = 1 × 32bit word.
        DWT->FUNCTION0 = (6<<0) // generate watch point debug event on WO match
            | (0<<7) // FUNCTION0.CYCMATCH == 0 for address comparison
            | (0<<8) // FUNCTIONn.DATAVMATCH == 0 for address comparison
            | (1<<5) // FUNCTIONn.EMITRANGE == 1 for trace packet generation
            ;
    }
}

static void watch_corruption_reset_watchpoint(void)
{
    DWT->FUNCTION0 = 0;
}

void watch_corruption_enable(void)
{
    nrf_atomic_flag_set(&watch_corruption);
}

void watch_corruption_disable(void)
{
    if (nrf_atomic_flag_clear_fetch(&watch_corruption)) {
        watch_corruption_reset_watchpoint();
    }
}

The watch_corruption_set_watchpoint is called after each valid writing to the potentially corrupted memory, so that a valid write won't trigger the watchpoint, and I am also sure that no subsequent valid writing can occur before watch_corruption_disable is called.

Now, I am debugging with the Segger JTracePro with Ozone IDE (so I compiled my FW with ENABLE_TRACE #define'd). My hope was after the watchpoint is triggered I can browse back the instruction trace to find what made the corruption.

So, my problem is the following : instead of what I expected/hoped to get, I get Ozone more or less frozen at some point of time, I cannot stop the debugging session. I need to for the Ozone application stop from system. So, it seems that what I am doing it messing up how Ozone interfaces with the JTracePro.

I would like to know how to do it properly. 

  • Answering to myself : I did the experiment again, and actually Ozone does not get frozen --- that was probably another issue --- but the debugger does not halt and show the instruction where the watchpoint is hit, although I can see in the RTTlogging that the corruption is still happening by testing the memory in the sequel. 

  • Once again answering to myself. I did the following plain experiment, calling the test_dwt  function below close to the very beginning of my application (but after logger init).

    void test_dwt(void)
    {
        NRF_LOG_WARNING("DWT->CTRL = 0x%08x",DWT->CTRL);
    
        uint32_t dummy;
    
        DWT->COMP0 = &dummy;
        DWT->MASK0 = 0;
        DWT->FUNCTION0 = (6<<0) // generate watchpoint data trace packet for WO access
            | (0<<7) // FUNCTION0.CYCMATCH == 0 for address comparison
            | (0<<8) // FUNCTIONn.DATAVMATCH == 0 for address comparison
            | (0<<5) // FUNCTIONn.EMITRANGE == 0 for trace packet conveying PC value & data value
            ;
    
        *(uint32_t volatile*)&dummy = 0;
    
        NRF_LOG_WARNING("DWT->FUNCTION0 = 0x%08x",DWT->FUNCTION0);
        DWT->FUNCTION0 = 0;
    
        NRF_LOG_FLUSH();
        for(;;) {}
    }

    Here is what I get in the output Cry

    <warning> app: DWT->CTRL = 0x0000978C
    <warning> app: DWT->FUNCTION0 = 0x0000978C
    Transfer rate: 0 KByte/s Bytes written: 105 Byte   

    The 3 MSBits of DWT->CTRL are the DWT_CTRL.NUMCOMP field which in ARM v7M reference manual are documented as giving the number of implemented comparators. According to nRF52840 data sheet there are two comparators, so this should be 2 instead of 0. Then why so ?

    Maybe there is some power domain setting to enable to whole debugging peripheral ?

  • Hello,

    This is something I need to look more into as I don't know the answer. But I suspect there is a conflict between the debugger and code as both try to access DWT.

  •  Thank you for paying attention to my ticket.

    In the latest experiment (that in which I call some test_dwt function) there is no debugger running, just the RTTlogger through a JLink probe.

    BTW I did some mistake in the code comments, but I cannot re-edit my posts to fix them. 

  • To ensure the device is not in debug mode, could you try to do a pin-reset or power-cycle prior to running this test? I've previously tried using DWT as a stack guard to catch stack overflows, and I think I remember some similiar problems.

    Here's the code I used:

    #define STACK_LIMIT 0x2003e000
    
    void DebugMon_Handler(void)
    {
        NRF_LOG_INFO("Debug Monitor interrupt triggered");
        NRF_LOG_INFO("Current SP 0x%x", __get_MSP());
        NRF_LOG_FINAL_FLUSH();
        for(;;);
    }
    
    
    static void dwt_stack_guard_init()
    {
        uint32_t num;
    
        CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk | CoreDebug_DEMCR_MON_EN_Msk;
    
        NRF_LOG_INFO("CoreDebug->DEMCR : 0x%x", CoreDebug->DEMCR);
        
        /* Using the highest programmable int. priority which is reserved to the Softdevice, and
           because of that we must configure the priority after ble_stack_init(). This bypasses the
           Softdevice's internal int. priority validation check. */
        NVIC_SetPriority(DebugMonitor_IRQn, _PRIO_SD_HIGH); 
        NVIC_EnableIRQ(DebugMonitor_IRQn);
    
        num = (DWT->CTRL & DWT_CTRL_NUMCOMP_Msk) >> DWT_CTRL_NUMCOMP_Pos;
        NRF_LOG_INFO("Number of DWT comparators: %d", num);
    
        DWT->COMP0 = STACK_LIMIT;
        DWT->MASK0 = 4;
        DWT->FUNCTION0 = (7 << DWT_FUNCTION_FUNCTION_Pos);
         
        NRF_LOG_INFO("DWT->COMP0 : 0x%x", DWT->COMP0);
        NRF_LOG_INFO("DWT->MASK0 : 0x%x", DWT->MASK0);
        NRF_LOG_INFO("DWT->FUNCTION0 : 0x%x", DWT->FUNCTION0);
    
    }

Related