0

I am working on code that runs on an ARM Cortex M3. I am using a Lauterbach debugger that works with TRACE32 software package.

I am inserting a software breakpoint as ARM suggests (https://www.keil.com/support/man/docs/armcc/armcc_chr1359124993371.htm) as follows:

volatile uint16_t haris_4=0;

void function(void)
{
    haris_4 = 1;
    __breakpoint(0);
    haris_4 = 2;
...

I then go to TRACE32 debugger and observe the value of haris_4 variable. It has the value 1. This means that the breakpoint has actually hit.

I am trying to step over the breakpoint and continue execution of the program but I cant. The butttons of trace32 Single Step, Step over Call and Go dont work.

Any ideas how can I step over a software breakpoint and continue execution?

Haris Pap
  • 85
  • 1
  • 3
  • 10
  • Are you sure you need a hard-coded breakpoint? If your intention is to observe the value of a variable, then the usual approach would be to set a breakpoint with the debugger. This can be done either by double-click on the desired location in the List window, or via the Break.Set command. – Reinhard Weiss Jan 22 '21 at 13:08

1 Answers1

2

While the question sounds simple, there are actual several aspects to consider:

  1. Why does someone want to have a breakpoint instruction in his/her code?
  2. Is the processor in debug mode when executing the breakpoint instruction?
  3. Is a rather old version of TRACE32 used?

Note: When you write __breakpoint(0) in your C/C++ the compiler will actually create the assembler instruction BKPT 0 for it. (You can check the assembler code by clicking on the button "Mode" of the List window in TRACE32.)

So first of all: Is there a good reason for having __breakpoint(0) in your source code? I tend to say: No, under normal circumstances a static breakpoint instruction makes no sense inside the application code. If you want to stop at some instruction to observe your variables, set a breakpoint in TRACE32 with command Break.Set or go to the menu → Break → Set...
If you need to have the breakpoint inside the flash, the debugger will automatically set an onchip breakpoint. (TRACE32 also allows you to set software breakpoints in flash, if you really want that to that (but that is a little bit more tricky).)

However if you really want to have __breakpoint(0) in your source code, let's see why this might not work as you like...

Let's have look on the second point: Is the processor in debug mode when executing the breakpoint instruction?
If you execute the code before you connect the debugger, your CPU is probably not in debug mode when executing the BKPT instruction. In this case the CPU will jump to an exception vector. Depending on your configuration of the Debug Halting Control and Status Register (DHCSR) this will be an DebugMonitor or HardFault exception. In both cases you program counter is probably no longer in your function and when you push the step-buttons in TRACE32, some totally different code is executed. (Read more about this in the chapter "Debug event behavior" of the "ARMv7-M Architecture Reference Manual")
Please check if your Program Counter (PC) is actually still pointing to the BKPT instruction when you hit the step-buttons in TRACE32. (E.g. check the value for PC in menu → View → Register.) If the PC value is fine, this is probably an other issue...

So last: Is your TRACE32 version rather old? And what we can do in this case...

When a Cortex-M CPU executes a BKPT instruction in debug mode, the CPU will stop at that BKPT instruction and Program Counter (PC) will point to that BKPT instruction. Also single stepping on a BKPT instruction will let the PC stay on the BKPT instruction. While this behavior of the CPU is nice if the BKPT instruction got inserted dynamically by a debugger, it might be quite annoying if the BKPT instruction was added on purpose to the source code, because you can't step over it.

Thus, TRACE32 does a little trick and sets the Program Counter to the instruction beneath the BKPT instruction, when a BKPT instruction was executed. Thus, the BKPT instruction behaves like a NOP when single stepping.
However you have to used TRACE32 release R.2014.09 or newer.

If your TRACE32 version if older (so you haven't updated since 6 years) then you can still get it working with a PRACTICE script:

  • Create a text file and name it e.g. myfix.cmm
  • Enter the following two code lines to that text file:
    IF (ADDRESS.INSTR.LEN(P:R(PP))==2)&&((DATA.WORD(P:R(PP))&0xff00)==0xbe00)
    Register.Set PP R(PP)+2
    This script checks if the instruction at the current program pointer (PP) has a size of two bytes and if it has the Thumb machine code of a BKPT instruction. If yes, the script will increase the program pointer by two ("jumping" to the next instruction).
  • Execute the following command in the TRACE32 command line:
    GLOBALON PBREAK DO myfix.cmm
    That command will run your script myfix.cmm when the program execution stops e.g. because of a BKPT instruction.You might want to add that GLOBALON command to the PRACTICE script you execute to start your debug session.
  • And that's it: Next time the CPU stops because of a BKPT instruction, it will feel like it has stopped after the BKPT instruction and behave like a NOP when stepping.

However, if you have already TRACE32 from 2015 or newer, please also check the window PMACRO in TRACE32 to ensure that none or your colleagues has pranked your with another kind of GLOBALON PBREAK handler....

Holger
  • 3,920
  • 1
  • 13
  • 35