pyOCD icon indicating copy to clipboard operation
pyOCD copied to clipboard

"reset" to nRF52840 does not reset everything

Open rgrr opened this issue 1 year ago • 6 comments

Currently playing around with Sysview...

After flashing the test program below via pyocd to a nRF52840 target, DWT->CYCCNT is initialized and running as expected. After issuing a reset command via pyocd with the same firmware in flash, CYCCNT is not running. Specifying the reset mode does not help.

Doing a reset with openocd, CYCCNT is running as expected.

Investigating a little bit further showed, that inserting a delay in main() before init of CYCCNT solves the problem.

Is order of init / disconnect wrong?

/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>

#include <unistd.h>
#include <fcntl.h>

//#include "SEGGER_RTT.h"
#include "SEGGER_SYSVIEW.h"



static void _Delay(int period)
{
    volatile int i = (100000 / 17) * period;
    do {
        ;
    } while (i--);
}   // _Delay



#define SYSVIEW_DEVICE_NAME "PCA10056 Cortex-M4"
#define SYSVIEW_APP_NAME "SysView Games"


/* DWT (Data Watchpoint and Trace) registers, only exists on ARM Cortex with a DWT unit */
#define KIN1_DWT_CONTROL             (*((volatile uint32_t*)0xE0001000))
  /*!< DWT Control register */
#define KIN1_DWT_CYCCNTENA_BIT       (1UL<<0)
  /*!< CYCCNTENA bit in DWT_CONTROL register */
#define KIN1_DWT_CYCCNT              (*((volatile uint32_t*)0xE0001004))
  /*!< DWT Cycle Counter register */
#define KIN1_DEMCR                   (*((volatile uint32_t*)0xE000EDFC))
  /*!< DEMCR: Debug Exception and Monitor Control Register */
#define KIN1_TRCENA_BIT              (1UL<<24)
  /*!< Trace enable bit in DEMCR register */


#define KIN1_InitCycleCounter()      KIN1_DEMCR |= KIN1_TRCENA_BIT
  /*!< TRCENA: Enable trace and debug block DEMCR (Debug Exception and Monitor Control Register */

#define KIN1_ResetCycleCounter()     KIN1_DWT_CYCCNT = 0
  /*!< Reset cycle counter */

#define KIN1_EnableCycleCounter()    KIN1_DWT_CONTROL |= KIN1_DWT_CYCCNTENA_BIT
  /*!< Enable cycle counter */

#define KIN1_DisableCycleCounter()   KIN1_DWT_CONTROL &= ~KIN1_DWT_CYCCNTENA_BIT
  /*!< Disable cycle counter */

#define KIN1_GetCycleCounter()       KIN1_DWT_CYCCNT
  /*!< Read cycle counter register */



static void _cbSendSystemDesc(void) {
    SEGGER_SYSVIEW_SendSysDesc("N=" SYSVIEW_APP_NAME ",D=" SYSVIEW_DEVICE_NAME ",O=None");
    SEGGER_SYSVIEW_SendSysDesc("I#15=SysTick");
}



void SEGGER_SYSVIEW_Conf(void)
{
    //SEGGER_RTT_Init();
    KIN1_InitCycleCounter();
    KIN1_ResetCycleCounter();
    KIN1_EnableCycleCounter();

    SEGGER_SYSVIEW_Init(64000000, 64000000, NULL, _cbSendSystemDesc);
    SEGGER_SYSVIEW_SetRAMBase(0x20000000);
}   // SEGGER_SYSVIEW_Conf



int main()
{
    //_Delay(200);                   // this delay is required to have a running CYCCNT after reset

    SEGGER_SYSVIEW_Conf();

    SEGGER_SYSVIEW_Start();
    SEGGER_SYSVIEW_EnableEvents(0xffff);
    _Delay(222);

    SEGGER_SYSVIEW_Error("Start\n");
    for (int i = 0;  i < 30;  ++i) {
        SEGGER_SYSVIEW_MarkStart(0x1111);
        SEGGER_SYSVIEW_MarkStart(0x3333);
        SEGGER_SYSVIEW_WarnfTarget("cyccnt %d %u\n", i, KIN1_GetCycleCounter());
        SEGGER_SYSVIEW_MarkStop(0x3333);
        SEGGER_SYSVIEW_MarkStart(0x2222);
        _Delay(87);
        SEGGER_SYSVIEW_MarkStop(0x2222);
        SEGGER_SYSVIEW_MarkStop(0x1111);
    }

    SEGGER_SYSVIEW_DisableEvents(0xffff);
    SEGGER_SYSVIEW_Print("Stop\n");
    SEGGER_SYSVIEW_Stop();

    for (;;) {

    }
}   // main

rgrr avatar Apr 30 '23 15:04 rgrr

Hi @rgrr

I'm pretty sure I know what it is… Currently on disconnect, DEMCR.TRCENA is written to 0 to disable extra debug logic, including DWT. During connect, there would be some delay before DEMCR.TRCENA is set to 1. The exact timing and sequence of events, I'm not sure about without fully reproducing your setup.

This should only happen if you are calling pyocd reset or equivalent, so that it's performing a full connect/disconnect sequence. Doing a reset while pyocd remains connected, eg in gdb, should not affect debug logic at all.

There are two related changes that should probably be made:

  1. Don't clear DEMCR.TRCENA if the resume_on_disconnect option is False (default is True).
  2. Add an option to not clear DEMCR.TRCENA on disconnect. In many cases this should be cleared, so that the device is restored to non-debug mode and debug logic isn't consuming power.

Short term, you could add a line to your code to write DEMCR.TRCENA to 1. (It should actually do this anyway, to guarantee availability of DWT.)

flit avatar Apr 30 '23 22:04 flit

See #1541 for a patch that changes this. Just the first of the two changes above. On second thought, it might be best to leave the second change to a user script via the stop_core_debug delegate function.

In the process, I realised an issue with the debug sequence support I just merged: the DebugCoreStop standard sequence assumes that DHCSR and DEMCR must be cleared, therefore resuming the core and disabling debug logic, making it incompatible with leaving the core halted on disconnect. 😄

flit avatar Apr 30 '23 22:04 flit

Hello Chris

I have pulled the version of #1541 and tested reset with both -Oresume_on_disconnect=false/true. No success. CYCCNT is always stuck.

In my code TRCENA is already set. It is hidden in the function macro KIN1_InitCycleCounter().

Any suggestions what to test further?

rgrr avatar May 01 '23 07:05 rgrr

Hmm, that's unfortunate. Still #1541 was a useful change, so thanks for helping identify it.

What doesn't make sense here is why your code in SEGGER_SYSVIEW_Conf() doesn't always successfully enable the cycle counter. Regardless of what pyocd is doing during connection, this should work. (Because both pyocd and your code are performing read-modify-writes of DEMCR and DWT_CTRL, there's a possibility of concurrency issues, but that would probably be very intermittent.) So there must be something in the usage sequence that I'm missing.

What are the sequence of pyocd commands that you are using? If you have a project with this code that you could attach, that would also be helpful.

flit avatar May 02 '23 21:05 flit

Yes, you are right, it is weird, that CYCCNT is not running. Even more that it is stuck at some value. Could it be possible, that pyOCD starts the target and after that clears the TRCENA bit? Or would the counter value than again zero?

I'm issuing just a simple "pyocd reset -t nrf52840"

Example project is here: https://github.com/rgrr/playground/tree/feature/xray/tools/SystemView

rgrr avatar May 03 '23 06:05 rgrr

Pyocd doesn't clear TRCENA anywhere except on exit, as we've discussed. All other places where it writes DEMCR (mostly related to vector catch) use read-modify-writes with masks to set/clear bits, so won't touch TRCENA.

CYCCNT would be stuck if it had been running at some point, then DWT_CTRL.CYCCNTEN is cleared. I think the effect of clearing TRCENA in that case will vary depending on the special CPU variant and MCU clocking design, not entirely sure.

Thanks for the project link.

flit avatar May 03 '23 19:05 flit