How to detect if LPClink is attached ?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to detect if LPClink is attached ?

2,558 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by DaveNadler on Sun May 15 12:59:41 MST 2011
[FONT=Arial]A build including debug_printf starts and runs happily on power-up, but stops at the first debug_printf if LPClink is not attached. This isn't so urgent, but it would be really nice if my development build could run whether or not LPClink is present and still include debug_printf's. I'd like to wrap debug_printf etc with something that suppresses them if LPClink isn't connected.

So - Is there an easy way to check if LPClink is attached ?
This is using CodeRed semi-hosted Redlib on LPC11C14.

Thanks !
Best Regards, Dave
[/FONT]
7 Replies

1,569 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by DaveNadler on Tue Jul 12 08:00:06 MST 2011
[FONT=Arial]OK, I was frightened at first, but I merged this with the other
required hardfault handler updates and it works great.
Complete code below...

Thanks for the support !
Best Regards, Dave
[/FONT]
/*
Unlike Cortex-M3, on Cortex-M0 it is not possible to access the debug
registers to find out if a debug probe (like LPC-link) is attached. Only
debug tools can access these registers.

At the "bottom layer" of the C library, semihosting is implemented by a
"BKPT 0xAB" instruction. When the debug tools are not connected, this
instruction triggers a hard fault - and the default hard fault handler
just contains an infinite loop. This is why your application appears to
hang.

But what we can do instead, is provide a hard fault handler which
instead looks to see what the instruction that caused the hard fault was
- and if it was a "BKPT 0xAB", then it instead returns back to the user
application.

--- CodeRed support June 2011.

*/

void HardFault_HandlerC(unsigned long *hardfault_args);
// Use the 'naked' attribute so that C stacking is not used.
__attribute__((naked)) void HardFault_Handler(void){
  /*
   * Get the stack pointer that was in use (MSP or PSP), depending on
   * current operating mode, and pass it as the parameter to the C handler.
   * This function will never return EXCEPT if it was an unhandled
   * debugger breakpoint (which happens if no debug probe is attached).
   */
  __asm(".syntax unified\n"
    // Check which stack is in use
        "MOVS   R0, #4  \n"
        "MOV    R1, LR  \n"
        "TST    R0, R1  \n"
        "BEQ    _MSP    \n"
        "MRS    R0, PSP \n"
        "B      _checkFaultInstruction \n"
    "_MSP:  \n"
        "MRS    R0, MSP \n"
    "_checkFaultInstruction: \n"
    // Load the instruction that triggered hard fault
        "LDR    R1,[R0,#24] \n" // R1 = address of instruction that caused fault
        "LDRH   R2,[r1]     \n"
        // Semihosting instruction is "BKPT 0xAB" (0xBEAB)
        "LDR    R3,=0xBEAB  \n"
        "CMP    R2,R3       \n"
    // If it is NOT a BKT 0xAB, jump to the terminal hardfault handler
        "BNE    HardFault_HandlerC  \n"
    // Was semihosting instruction, so adjust location to
    // return to by 1 instruction (2 bytes), then exit function
        "ADDS   R1,#2       \n"
        "STR    R1,[R0,#24] \n"
        "BX     LR          \n"
        ".syntax divided\n") ;
}

/**
 * HardFaultHandler_C:
 * This is called from the HardFault_HandlerAsm with a pointer the Fault stack
 * as the parameter. We can then read the values from the stack and place them
 * into local variables for ease of reading.
 * We then read the various Fault Status and Address Registers to help decode
 * cause of the fault.
 * The function ends with a BKPT instruction to force control back into the debugger
 */
void HardFault_HandlerC(unsigned long *hardfault_args) {
  volatile unsigned long stacked_r0 ;
  volatile unsigned long stacked_r1 ;
  volatile unsigned long stacked_r2 ;
  volatile unsigned long stacked_r3 ;
  volatile unsigned long stacked_r12 ;
  volatile unsigned long stacked_lr ;
  volatile unsigned long stacked_pc ;
  volatile unsigned long stacked_psr ;
  volatile unsigned long _CFSR ;
  volatile unsigned long _HFSR ;
  volatile unsigned long _DFSR ;
  volatile unsigned long _AFSR ;
  volatile unsigned long _BFAR ;
  volatile unsigned long _MMAR ;

  stacked_r0 = ((unsigned long)hardfault_args[0]) ;
  stacked_r1 = ((unsigned long)hardfault_args[1]) ;
  stacked_r2 = ((unsigned long)hardfault_args[2]) ;
  stacked_r3 = ((unsigned long)hardfault_args[3]) ;
  stacked_r12 = ((unsigned long)hardfault_args[4]) ;
  stacked_lr = ((unsigned long)hardfault_args[5]) ;
  stacked_pc = ((unsigned long)hardfault_args[6]) ;
  stacked_psr = ((unsigned long)hardfault_args[7]) ;

  // Configurable Fault Status Register
  // Consists of MMSR, BFSR and UFSR
  _CFSR = (*((volatile unsigned long *)(0xE000ED28))) ;   

  // Hard Fault Status Register
  _HFSR = (*((volatile unsigned long *)(0xE000ED2C))) ;

  // Debug Fault Status Register
  _DFSR = (*((volatile unsigned long *)(0xE000ED30))) ;

  // Auxiliary Fault Status Register
  _AFSR = (*((volatile unsigned long *)(0xE000ED3C))) ;

  // Read the Fault Address Registers. These may not contain valid values.
  // Check BFARVALID/MMARVALID to see if they are valid values
  // MemManage Fault Address Register
  _MMAR = (*((volatile unsigned long *)(0xE000ED34))) ;
  // Bus Fault Address Register
  _BFAR = (*((volatile unsigned long *)(0xE000ED38))) ;

  __asm("BKPT #0\n") ; // Break into the debugger
}

1,569 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by larryvc on Thu May 19 16:41:34 MST 2011
It works on the Cortex M3 too.  Only tested on the LPC1769.
0 Kudos

1,569 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by CodeRedSupport on Thu May 19 04:51:31 MST 2011
First the bad news. I've now dug deeper and also discussed this matter with ARM - and basically it is not possible to access the debug registers in this way on Cortex-M0 (unlike Cortex-M3). Only debug tools can access these registers.

This fact is buried deep in the ARM  v6M Architecture Reference Manual - and I unfortunately didn't spot this the first time around. It doesn't appear to be documented in the technical reference manual for Cortex-M0 though, and ARM have raised a defect on the documentation to make this all clearer. I'll also go back to the group who looks after the CMSIS spec in ARM, and request that the debug registers get removed from the CMSIS headers for Cortex-M0 too.

It therefore appears that it is not going to be possible to detect when LPC-Link is connected for Cortex-M0 based systems. However, if we take a slightly different approach, then there is a "solution" that might work for you.

At the "bottom layer" of the C library, semihosting is implemented by a "[FONT=Courier New][SIZE=1]BKPT 0xAB[/SIZE][/FONT]" instruction. When the debug tools are not connected, this instruction triggers a hard fault - and the default hard fault handler just contains an infinite loop. This is why your application appears to hang.

But what we can do instead, is provide a hard fault handler which instead looks to see what the instruction that caused the hard fault was - and if it was a "[FONT=Courier New][SIZE=1]BKPT 0xAB[/SIZE][/FONT]", then it instead returns back to the user application.

The below example hard fault handler will do this for you. Simply add a file, say "[FONT=Courier New][SIZE=2]hardfault_semihost.c[/SIZE][/FONT]", to your project, and copy this code into it. Then build - and your semihosted applications should then execute without the debug tools attached.


// Cortex-M0 Hard fault handler which allows applications which
// contain semihosting operations to execute, even when debug
// tools are not connected (rather than just sitting in infinite
//  loop in hard fault handler)

__attribute__((naked))
void HardFault_Handler(void){
        __asm(  ".syntax unified\n"
                // Check which stack is in use
                        "MOVS   R0, #4  \n"
                        "MOV    R1, LR  \n"
                        "TST    R0, R1  \n"
                        "BEQ    _MSP    \n"
                        "MRS    R0, PSP \n"
                        "B      _process      \n"
                "_MSP:  \n"
                        "MRS    R0, MSP \n"
                // Load the instruction that triggered hard fault
                "_process:     \n"
                        "LDR    R1,[R0,#24] \n"
                        "LDRH    R2,[r1] \n"
                // Semihosting instruction is "BKPT 0xAB" (0xBEAB)
                        "LDR    R3,=0xBEAB \n"
                        "CMP     R2,R3 \n"
                        "BEQ    _semihost_return \n"
                // Wasn't semihosting instruction so enter infinite loop
                        "B . \n"
                // Was semihosting instruction, so adjust location to
                // return to by 1 instruction (2 bytes), then exit function
                "_semihost_return: \n"
                        "ADDS    R1,#2 \n"
                        "STR    R1,[R0,#24] \n"
                        "BX LR \n"
                ".syntax divided\n") ;
}
[B]WARNING - I have tested this code out with a printf on an LPC1114, but I can imagine  that this approach might not work with all semihosting operations (for example anything where your application might rely upon the value that the semihosting operation would normally return, such as a scanf).[/B]

Regards,
CodeRedSupport
0 Kudos

1,569 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by CodeRedSupport on Tue May 17 12:32:32 MST 2011

Quote: Zero
This program is working without problems with LPC1769:)

Unfortunately it fails with LPC11C14 and LPC1114:mad:



Indeed - I unfortunately only tested on LPC17 before posting - I did check the docs and CMSIS files for M0, but didn't actually run the example.

Anyway, we'll investigate.

Regards,
CodeRedSupport
0 Kudos

1,569 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Tue May 17 10:57:05 MST 2011
This program is working without problems with LPC1769:)

Unfortunately it fails with LPC11C14 and LPC1114:mad:
0 Kudos

1,568 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by DaveNadler on Tue May 17 09:44:47 MST 2011
[FONT=Arial]Hi Guys - Unfortunately the following code, when run with LPClink attached, blinks the LED:
[/FONT][INDENT][FONT=Lucida Console]  // The C_DEBUGEN bit in the Debug Halting Control and Status Register[/FONT]
[FONT=Lucida Console]  // (DHCSR) will be set when there is a debug connection active from the IDE.[/FONT]
[FONT=Lucida Console]  if ((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk)==CoreDebug_DHCSR_C_DEBUGEN_Msk) {[/FONT]
[FONT=Lucida Console]    // Action to take when debug connection active[/FONT]
[FONT=Lucida Console]    debug_printf("LPClink is active...\n");[/FONT]
[FONT=Lucida Console]  }[/FONT]
[FONT=Lucida Console]  else {[/FONT]
[FONT=Lucida Console]    // Action to take when debug connection inactive[/FONT]
[FONT=Lucida Console]    // Flash LED to indicate that this condition was detected...[/FONT]
[FONT=Lucida Console]    int t=20;[/FONT]
[FONT=Lucida Console]    while(t--) {[/FONT]
[FONT=Lucida Console]      int delay=40000;[/FONT]
[FONT=Lucida Console]      while(delay--) {};[/FONT]
[FONT=Lucida Console]      BVM2_LED_TOGGLE;[/FONT]
[FONT=Lucida Console]    };[/FONT]
[FONT=Lucida Console]  }[/FONT]
[/INDENT][FONT=Arial]What am I doing wrong ?
Thanks,
Best Regards, Dave


[/FONT]
0 Kudos

1,568 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by CodeRedSupport on Mon May 16 01:44:51 MST 2011

Quote: DaveNadler
[FONT=Arial]So - Is there an easy way to check if LPClink is attached ?
This is using CodeRed semi-hosted Redlib on LPC11C14.
[/FONT]


You can check the [FONT=Courier New][SIZE=1]C_DEBUGEN[/SIZE][/FONT] bit in the Debug Halting Control and Status Register ([FONT=Courier New][SIZE=1]DHCSR[/SIZE][/FONT]). This bit will be set when there is a debug connection active from the IDE. The following code (which assumes you are including the CMSIS header file for your MCU) shows how this might be done...

if ((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk)==CoreDebug_DHCSR_C_DEBUGEN_Msk) {
       // Action to take when debug connection active
}
else {
       // Action to take when debug connection inactive
}



Note that if you had been using a Cortex-M3 based part, then the more advanced debug facilities on that architecture provide something called Serial Wire Viewer. When using RedSuite with RedProbe+ (rather than LPCXpresso/LPC-Link), this then provides an output mechanism within our RedTrace functionality called "hoststrings". This allows output information to be sent out via the Instrumentation Trace Macrocell (ITM), and this can actually be left in place without ill effect when the debug tools are not connected.

Regards,
CodeRedSupport
0 Kudos