Software and Hardware Breakpoints

Using breakpoints is central part of debugging. I’m usually debugging my applications in flash memory. Because nearly all the microcontrollers I use have on-chip flash memory, and have more flash than RAM. With debugging in flash I limited by the number of hardware breakpoints. And here is the advantage with debugging code in RAM: availability of ‘unlimited’ software breakpoints. But how does this all works, and how to make efficient usage of hardware breakpoints?

Software Breakpoints

With software breakpoints, the debugger replaces an instructions with an illegal instruction or with a dedicated breakpoint instruction supported by the instruction set of the microcontroller. To illustrate this, below is some code in RAM starting at address 0x100. The LDA load instruction from the address 0x80 is encoded as 0x3788:

Code in RAM

Code in RAM

I want to set a breakpoint on the multiply MULA instruction at address 0x102. Assuming that this (hypothetical) architecture has a breakpoint instruction encoded as 0xffff, the debugger simply can replace the opcode at address 0x102 with the 0xffff BKPT opcode:

Code in RAM with Software Breakpoint

Code in RAM with Software Breakpoint

If the processor runs the code at address 0x102, it will trigger the debug module as defined in the microcontroller architecture.

I can take advantage of this as well in my application code. For example the ARM Cortex architecture supports the BKPT instruction. I can use this to have present in an interrupt routine to stop my target: The processor will cause an ‘illegal instruction’ exception which the debugger can catch.

void __attribute__((interrupt)) ISR_Unhandled(void) {
  asm("BKPT 255"); /* sofware breakpoint */
}

As a user, I do see that replaced instruction: the debugger does the replacement just as part of restarting the processor. And the debugger will restore the original content after the processor has stopped. The debugger maintains a list of places where it has replaced the memory with the breakpoint instructions:

Software Breakpoint List in Debugger

Software Breakpoint List in Debugger

If the debugger fails to restore the original instruction, then the software breakpoints remains and causes a target halt when it reaches that instruction. The debugger might report “illegal breakpoint” or something similar depending on the debugger. The problem might be caused by a board or hardware problem. Then the debugger breakpoint list might be corrupted too, and in this case it is best to reload the application to the target.

The good thing with software breakpoint is that it provides a almost unlimited number of breakpoints. And because changing instructions in RAM is easy for the debugger, software breakpoints are used for applications in RAM.

Some debuggers even apply software breakpoints on code running in flash memory. But this usually will make setting and removing breakpoints a time-consuming thing: The debugger has to erase and re-program the flash to set and remove the breakpoints. Other architectures implement a ‘patch list’, similar what is shown above with the debugger breakpoint list: with this it is possible to set software breakpoints with hardware. Which leads us to ‘real’ hardware breakpoints.

Hardware Breakpoints

To make debugging in flash memory easier, silicon vendors have added hardware breakpoint capabilities to their cores. Typical hardware breakpoints watch an internal bus or the program counter, and if it matches a certain condition, it will stop the processor, or will do whatever the hardware implements for that condition.

Hardware Debug Trigger

Hardware Debug Trigger

With this hardware capability, the debugger needs to know that particular device Hardware Debug Trigger module to set the flags. However, the number of hardware debug triggers is limited. The ARM Cortex-M0+ which is on my Freedom board is no exception to this: ARM has implement hardware debug support in the CoreSight module. The ARM Cortex-M0+ CoreSight can have 1, 2, 3 or 4 hardware breakpoints implemented. So I have software and hardware breakpoints, but do I have any control over what is used by the debugger?

Choosing Breakpoint Type

In the CodeWarrior debugger it is possible to affect the choice of debugger: the context menu in the ‘blue’ annotation area on the left side of the source let me select what kind of breakpoint is used:

Breakpoint Type Menu

Breakpoint Type Menu

This especially useful if I’m running an application with parts in RAM and parts in FLASH, or if I want to use hardware breakpoints for code in RAM.

Hardware Breakpoints in Eclipse

In Eclipse it is the Breakpoints View which manages the list of breakpoints:

Eclipse Breakpoints View

Eclipse Breakpoints View

What happens if I have only 2 hardware breakpoints, but I’m going to set 4. In the example below I set a breakpoint on line 41, then 42, then 43 and finally on line 40:

4 Breakpoints

4 Breakpoints

Only two breakpoints will have the check mark set:

4 Breakpoints, but only 2 are set

4 Breakpoints, but only 2 are set

This is shown in the Breakpoints view too:

Breakpoints View

Breakpoints View

Obviously the debugger is using Hardware breakpoints, according to the ‘Type’ column, and only two are set successfully. Having too many breakpoints set will have some impact on debugging.

Note: I pay close attention to the Breakpoints View and if my breakpoints have the check mark set or not. Because debugging a problem with a breakpoint not set I was thinking only I had set it is not very productive. And it allows me to closely monitor how many hardware breakpoints are used.

Running out of Hardware Breakpoints

If I launch my debug sessions with too many breakpoints, then the debugger is telling me about this:

Breakpoint Warning: Unable to set all breakpoints requested.

Breakpoint Warning: Unable to set all breakpoints requested.

As a counter measure, I can disable breakpoints from the Breakpoints view:

Disabled Breakpoints in Breakpoints View

Disabled Breakpoints in Breakpoints View

With all available my hardware breakpoints set, I still can do normal stepping. Well, not for all stepping cases, as I very likely will run into this one:

Target request failed: Failed to step.

Target request failed: Failed to step.

It means that the debugger was not able to set a breakpoint. What happened?

If I do a ‘step out’, the debugger needs an extra hardware breakpoint to stop the target if I step outside of the current function. But it if all my hardware breakpoints are already used, then the debugger cannot set that extra one.

A solution to this situation is to disable my hardware breakpoints in the Breakpoints view while stepping. But there is a small trick which can make my life easier :-).

Skipping Breakpoints Trick

This tipp does not solve all use cases, but helps. Usually the CodeWarrior debugger keeps the hardware breakpoints installed during stepping: that way if you ‘step over’ a function which has a breakpoint set, it will trigger inside that function. But if I’m OK to use my hardware breakpoints for the stepping operation, I can tell the CodeWarrior debugger to skip breakpoints (Window > Preferences > Run/Debug):

Skip Breakpoints during 'Run to Line' operation.

Skip Breakpoints during ‘Run to Line’ operation.

What it does is to disable all my set breakpoints when I do a ‘Run to Line’ stepping. This can be done with selecting a source line in the Source view and select the ‘Run to Line’:

Run to Line Context Menu

Run to Line Context Menu

An even easier view is to place the cursor in the Source view, and using the Ctrl+R shortcut. With this I do not need to disable my hardware breakpoints in the Breakpoints view, and still can do full debugger stepping. And as a side effect, I’m not running into my otherwise set breakpoints.

Happy Breakpointing 🙂

15 thoughts on “Software and Hardware Breakpoints

  1. Pingback: Debugger Shell: Test Automation | MCU on Eclipse

  2. Pingback: Skipping Breakpoints | MCU on Eclipse

  3. Pingback: New CodeWarrior for MCU10.5 | MCU on Eclipse

  4. Hi Erich, I have a problem when debugging a bareboard project at KL46Z. ¿Do you know why I need to press several times Step Over F6 to advance a debug line? Thanks!

    Like

    • I use OpenSDA Embebbed Debug – USB Port, but

      I tried again and works well, the number that was changing when pressing F6 is the one highlighted in the image.

      Thanks for the reply and congratulations for the site.

      Like

  5. Hello there, I’m having an issue related to RESET on MKL25 processor. Some times when I press reset button on my board the firmware stops on thumb_startup() and keeps waiting for a RUN command (F8 on CodeWarrior 10.5), even if the Segger is disconnected. How do I make the RESET button actually resets the processor and make it run after it? In a real applicattion the Segger hardware certainly will not be there… 😉 Could it be related to breakpoints?

    Like

    • Hi Rafael,
      the thing is that the debugger through the K20/OpenSDA on your board detects the reset, and halts the target. It won’t halt it if you do not have a debug session running. So if you are not debugging (e.g. the FRDM-KL25Z board), then the K20/OpenSDA is not active, and the KL25Z will run out of reset. Same thing with your custom hardware.

      Like

  6. Hi Erich

    I realise that this is an old post but just thought I’d comment regardless. 🙂
    I found this a little confusing because here you’re presumably dealing with some hypothetical CPU:

    > Assuming that this architecture has a breakpoint instruction encoded as 0xffff, the debugger simply can replace the opcode at address 0x102 with the 0xffff BKPT opcode:

    whereas later on you go on to deal specifically with ARM Cortex-M where the breakpoint instruction is:

    BKPT #imm8

    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/BABHCHGB.html

    and the opcode for the Cortex-M BKPT instruction is actually 0xbe.

    Might be worth clarifying just in case somebody assumes that the machine code encoding of a Cortex-M breakpoint instruction is 0xffffffff?

    Regards
    Tommy

    Like

    • Hi Peter,
      I have used things like
      __asm(“BKPT #0\n”) ; /* cause the debugger to stop */
      While you are asking: it seems that there is an issue in most recent GDB version which causes the debugger not to halt. It works fine for me in the GDB used in KDS v3.2.0:
      GNU gdb (GNU Tools for ARM Embedded Processors) 7.6.0.20140731-cvs

      Like

  7. Pingback: Tutorial: CRC32 Checksum with the KBOOT Bootloader | MCU on Eclipse

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.