Difference between revisions of "Delayed Program Start in Debug"

From SEGGER Wiki
Jump to: navigation, search
(Test & Verify functionality using J-Link Commander)
(Problem)
 
(9 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
__TOC__
 
__TOC__
   
In debug mode, users expect the MCU to be halted right before executing the target application. Usually, the J-Link archives this by configuring debug logic of the MCU accordingly. Of course, this assumes that a debug reset does not reset the debug logic. This applies for most MCUs but unfortunately not for all. For MCUs where the debug logic does not survive the reset, the J-Link has no chance to force the MCU to automatically halt after reset.
+
After issuing a reset in debug mode, the MCU is expected to halt right before executing the target application. Usually, the J-Link archives this by configuring debug logic of the MCU accordingly. Of course, this assumes that a debug reset does not clear the debug logic. This applies for most MCUs but unfortunately not for all. For MCUs where the debug logic does not survive the reset, the J-Link has no chance to force the MCU to automatically halt after reset.
   
 
==Problem==
 
==Problem==
After reset, the MCU automatically jumps to the user application and starts executing. The J-Link tries to re-enable the debug logic + halt the MCU as soon as possible but there is no guarantee that this can be done fast enough. In case of the application has been already started, any further action may result in unexpected behavior.
+
After reset, the MCU automatically jumps to the user application and starts executing. The J-Link tries to re-enable the debug logic + halt the MCU as soon as possible in such scenarios but there is no guarantee that this is done fast enough. In case of the application has been already started, any further action may result in unexpected behavior.
  +
In the following, some examples for "unexpected behavior" are given:
  +
* J-Link halts the CPU after the startup code has run and enabled the PLL + switched the clock to the PLL.<br> The IDE now may set the PC + SP to their initial values (reset vector, before startup code) and may re-run the startup code + PLL init.<br> As the CPU is already running on the PLL, this might not be expected by the PLL init code and the CPU may crash.
  +
* J-Link halts the CPU after the startup code has run and enabled the caches.<br> The IDE now may set the PC + SP to their initial values (reset vector, before startup code) and may re-run the startup code + cache init<br> but with caches already enabled. If some portions of the startup code rely on reset state (disabled cache), things may not work properly.
  +
* J-Link halts the CPU after some code in main() was already executed. This code may have enabled a motor control and interrupts.<br> The IDE now may set the PC + SP to their initial values (reset vector, before startup code),<br> so the motor control is not handled correctly while the startup code is re-executed.
  +
 
==Solution==
 
==Solution==
 
Add a delay loop (e.g. 1 second) in the beginning of the startup code. The idea is to give the J-Link a defined timeframe to re-enable the debug logic + perform the halt request before the actual application code is executed. We recommend to enable this delay loop in the debug configuration of your project, only.
 
Add a delay loop (e.g. 1 second) in the beginning of the startup code. The idea is to give the J-Link a defined timeframe to re-enable the debug logic + perform the halt request before the actual application code is executed. We recommend to enable this delay loop in the debug configuration of your project, only.
=== Instrument the Startup Code - Cortex-M ===
+
=== Instrumented Startup Code - Cortex-M ===
 
The following instructions needs to be added to the startup code. Please note that the number of the delay loops needs to be adjusted depending on the CPU frequency. In this example, a STM32F4 has been used which runs at 16 MHz after reset.
 
The following instructions needs to be added to the startup code. Please note that the number of the delay loops needs to be adjusted depending on the CPU frequency. In this example, a STM32F4 has been used which runs at 16 MHz after reset.
 
<source lang="c">
 
<source lang="c">
Line 13: Line 18:
 
// Execute a ~1 second delay loop
 
// Execute a ~1 second delay loop
 
//
 
//
#IF DEBUG
+
#if DEBUG
 
mov r0, 0x300000
 
mov r0, 0x300000
 
Delay_Loop:
 
Delay_Loop:
Line 21: Line 26:
 
#endif
 
#endif
 
</source>
 
</source>
  +
=== Instrument the Startup Code - Cortex-A/R ===
 
  +
=== Instrumented Startup Code - Cortex-A/R ===
 
TBD
 
TBD
   
=== Instrument the Startup Code - RISC-V ===
+
=== Instrumented Startup Code - RISC-V ===
 
TBD
 
TBD
   
== Example ==
+
== Example - Cortex-M ==
The following example project was created with the SEGGER Embedded Studio and runs out-of-the-box on SEGGERs Cortex-M Trace Reference Board. It is a simple blinky application which toggles LED0. It makes use of a generic startup code which has been instrumented as described above.
+
The following example project was created with the SEGGER Embedded Studio and runs out-of-the-box on SEGGERs Cortex-M Trace Reference Board. It is a simple blinky application which toggles LED0. It makes use of a generic startup code which has been instrumented as described above. Please note that this is a theoretical example because the STM32F4 *does not* clear the debug logic on reset. However, you can see that after a power on reset, it takes ~1 second until the actual application boots. This would be enough time to re-connect + issue a halt request on MCUs where the reset does clear the debug logic.
   
 
The project can be easily ported to any other Cortex-M based MCU.<br>
 
The project can be easily ported to any other Cortex-M based MCU.<br>
Line 36: Line 42:
 
*Hardware: SEGGER Cortex-M Trace Reference Board (ST STM32F407VE device)
 
*Hardware: SEGGER Cortex-M Trace Reference Board (ST STM32F407VE device)
 
*Project: [[File:SEGGER_Cortex-M_Trace_Reference_Board_Blinky__DelayedProgramStart_ES_V562.zip]]
 
*Project: [[File:SEGGER_Cortex-M_Trace_Reference_Board_Blinky__DelayedProgramStart_ES_V562.zip]]
 
===Test & Verify functionality using J-Link Commander===
 
#Download the application using Embedded Studio to the target
 
#Power-on reset the target
 
#You should see a 1 second delay before the actual application (led blinky) starts.
 
 
===Conclusion / Summary===
 
After performing the normal reset, the MCU is halted on the reset vector (Reset_Handler / 0x0800010E).<br>
 
After performing a reset via reset + halt after bootloader strategy, the MCU is halted on the instrumented startup code (0x08000116)<br>
 
'''This confirms that the reset behaves as expected.<br>'''
 
In order to simulate a bootloader, the instrumented code may be moved from the startup code to somewhere in the user application. Performing a reset should show that the CPU is halted at the address where the instrumented code has been moved to.
 

Latest revision as of 18:30, 6 December 2021

After issuing a reset in debug mode, the MCU is expected to halt right before executing the target application. Usually, the J-Link archives this by configuring debug logic of the MCU accordingly. Of course, this assumes that a debug reset does not clear the debug logic. This applies for most MCUs but unfortunately not for all. For MCUs where the debug logic does not survive the reset, the J-Link has no chance to force the MCU to automatically halt after reset.

Problem

After reset, the MCU automatically jumps to the user application and starts executing. The J-Link tries to re-enable the debug logic + halt the MCU as soon as possible in such scenarios but there is no guarantee that this is done fast enough. In case of the application has been already started, any further action may result in unexpected behavior. In the following, some examples for "unexpected behavior" are given:

  • J-Link halts the CPU after the startup code has run and enabled the PLL + switched the clock to the PLL.
    The IDE now may set the PC + SP to their initial values (reset vector, before startup code) and may re-run the startup code + PLL init.
    As the CPU is already running on the PLL, this might not be expected by the PLL init code and the CPU may crash.
  • J-Link halts the CPU after the startup code has run and enabled the caches.
    The IDE now may set the PC + SP to their initial values (reset vector, before startup code) and may re-run the startup code + cache init
    but with caches already enabled. If some portions of the startup code rely on reset state (disabled cache), things may not work properly.
  • J-Link halts the CPU after some code in main() was already executed. This code may have enabled a motor control and interrupts.
    The IDE now may set the PC + SP to their initial values (reset vector, before startup code),
    so the motor control is not handled correctly while the startup code is re-executed.

Solution

Add a delay loop (e.g. 1 second) in the beginning of the startup code. The idea is to give the J-Link a defined timeframe to re-enable the debug logic + perform the halt request before the actual application code is executed. We recommend to enable this delay loop in the debug configuration of your project, only.

Instrumented Startup Code - Cortex-M

The following instructions needs to be added to the startup code. Please note that the number of the delay loops needs to be adjusted depending on the CPU frequency. In this example, a STM32F4 has been used which runs at 16 MHz after reset.

//
// Execute a ~1 second delay loop
//
#if DEBUG
        mov r0, 0x300000
Delay_Loop:
        subs r0, #1
        cmp  r0, #0
        bne Delay_Loop
#endif

Instrumented Startup Code - Cortex-A/R

TBD

Instrumented Startup Code - RISC-V

TBD

Example - Cortex-M

The following example project was created with the SEGGER Embedded Studio and runs out-of-the-box on SEGGERs Cortex-M Trace Reference Board. It is a simple blinky application which toggles LED0. It makes use of a generic startup code which has been instrumented as described above. Please note that this is a theoretical example because the STM32F4 *does not* clear the debug logic on reset. However, you can see that after a power on reset, it takes ~1 second until the actual application boots. This would be enough time to re-connect + issue a halt request on MCUs where the reset does clear the debug logic.

The project can be easily ported to any other Cortex-M based MCU.

SETUP