Before you begin
To follow the instructions in this Learning Path, clone or download the
Blinky example project repo
on the same machine where you have installed Arm Keil MDK and the Corstone-300 Ecosystem FVP. Double-click the
Blinky.uvprojx file to open it in µVision.
To use hardware breakpoints in µVision follow the steps outlined below:
Build (F7) the example Blinky project you opened.
Start a Debug Session (Ctrl+F5) to enter the µVision debugger.
Run (F5) the application.
Blinky.c in focus by clicking on its tab. If it is not visible, double-click on it in the Project window.
Blinky.c, scroll down to the
while (1) loop in the
main function near line 42 as shown here:
- Note the darker grey blocks on the left of the line numbers. They indicate assembly code being present and that you can set a hardware breakpoint on these lines. You can also see these blocks in the Disassembly window:
- While the program is still running, click to the left of a suitable line in the
while loop (for example at line 47) and a red circle will be created. This is a hardware breakpoint. The simulated Arm Cortex-M55 has eight hardware breakpoints. μVision will warn you if you exceed this limit.
- The program will soon stop at the line where you have set the breakpoint on as shown below. The yellow arrow is the current program counter position. This will be the next instruction executed. The cyan arrow is a placeholder you can use to explore the relationship between a source window and the Disassembly window.
- Add another breakpoint in
while (1) (for example at line 50).
- Each time you click on
Run (F5), the program will cycle to the next breakpoint.
- Sometimes the compiler will optimize out code. Breakpoints cannot be set on this code as shown by the lack of grey blocks. Setting the compiler to an optimization level of
-O0 will usually help (this is set in this example project already).
- You can set/unset hardware breakpoints with μVision while the program is running or stopped.
- Arm hardware breakpoints are no skid. They do not execute nor change the instruction they are set to.
- Your instructions in flash are not substituted or modified. Your flash contents are not changed.
- There is no intrusion to hardware or your program by Arm hardware breakpoints.
- Go to Debug - Breakpoints (Ctrl-B) to manage breakpoints:
- You can temporarily unselect, delete or create breakpoints in this window. It is easier to create a breakpoint by clicking in a source file or the Disassembly window.
- Watchpoints are also created in this window.
- Select Kill All to remove all breakpoints.
- Click Close.
- Continue to
Run (F5) for the next exercise.
For breakpoint expression examples, refer to the
Call Stack + Locals Window
Call Stack + Locals
window displays call stack contents as well as any local variables belonging to the active function. If possible, the values of the local variables will be displayed. If this is not possible, a message such as
<not in scope> will be displayed. The Call Stack + Locals window visibility can be toggled by selecting View - Call Stack window.
Call Stack Use Case
When the program is stopped, the list of stacked functions is displayed. This is when you need to know which functions have been called and are stored on the stack. This is useful to find program flow problems such as crashes. Normal functions are displayed only when they are on the stack. They are removed and added as appropriate.
- Stop the program with the
Stop icon. The program will probably stop in the
- Click on the Call Stack + Locals window in the bottom right corner of μVision.
- Inspect the various entries in the Call Stack + Locals window as shown below in this simple example. Local variables are displayed only when they are in scope:
- Set a breakpoint in the
while (1) loop at line 50 on the
g_ledSet = 1;.
Run (F5) the application.
- Shortly after, the program will stop there.
Step (F11) with Blinky.c in focus (its name in its tab is underlined).
- Note how the variables displayed change in the Call Stack + Locals window.
Step (F11) a few more times.
- Right-click on a function and select either Show Caller Code or Show Callee Code and this will be highlighted in the Disassembly and source code windows:
Step Out (Ctrl+F11) to exit a function immediately.
- Remove the Breakpoint (by clicking on its red circle
) to continue.
- You can modify a variable value in the Call Stack & Locals window when the program is stopped.
- This window is only valid when the processor is halted. It does not update while the program is running because locals are normally kept in a CPU register which cannot be read by the debugger while the program is running. Any local variable values are visible only when they are in scope.
- This is standard “run/stop” debugging. With Arm CoreSight debug technology you can do more. You can display global or static variables and structures updated in real-time while the program is running. No additions or changes to your code are required. They only must be converted to global or static variables so they always remain in scope.
- When Keil RTX5 is used, all threads are displayed, even if they are not running. The active thread name and its number are highlighted in green.
Step (F11): If you click inside a source window to bring it into focus, the program will step one source line at a time. If you click inside the Disassembly window to bring it into focus, it will then step one instruction at a time.
Watch and Memory Windows
windows display variable values in real-time using Arm CoreSight technology. It is also possible to
put or insert values into these memory locations in real-time using the Memory window while the program is running. You can change memory in a Watch window if data is not changing fast.
There is a global variable
g_msTicks located in
Blinky.c near line 11 that you can use in the Watch and Memory windows.
Run (F5) the application.
- Right click on
Blinky.c near line 11 and select Add ‘g_msTicks’ to… and select Watch 1.
- Go to View in the main menu and enable Periodic Window Update:
g_msTicks will be displayed in Watch 1:
- The values of
g_msTicks are updated in real-time if Periodic Window Update is enabled.
- You can modify the value in a Watch window when the program is stopped or changing slowly. You can modify a variable in a Memory window anytime (see next section).
Add the SystemCoreClock Variable
- Click on
<Enter expression> twice and enter:
- Right click on the
Value and deselect Hexadecimal Display. 32 MHz will be displayed:
You do not need to stop the program execution to enter variables, raw addresses or structures in a Watch or Memory window.
- Right click on
g_msTicks and select Add ‘
g_msTicks ’ to… and select Memory 1.
- Note the changing value of
g_msTicks is displaying its address in Memory 1 as if it is a pointer. This is useful to see what address a pointer is pointing to. But this not what you want to see at this time.
- Right click in Memory 1 and select Unsigned Long to see the data field as 32-bit numbers.
- Add an ampersand
& in front of the variable name
g_msTicks and press Enter. Now the physical address is shown (0x2000_0008) in this case. This physical address could change with different compilation optimizations.
- The data contents of
g_msTicks is displayed as shown:
- Right click on the memory data value and select Modify Memory at 0x3000000C. Enter a value and this will be pushed into
g_msTicks is updated often, you will only see the new value displayed for a very short time.
Stop the application.
- You can configure the Watch and Memory windows while the program is running in real-time without using any CPU cycles. You can change a Memory window value on-the-fly.
- View - Periodic Window Update
must be selected. Otherwise variables update only when the program is stopped.
How It Works
μVision uses Arm CoreSight technology to read or write memory locations without using CPU cycles. Most of the time, this is non-intrusive and does not impact the program execution timings. The Armv7-M/Armv8-M are Harvard architectures. This means they have separate instruction and data buses. While the CPU is fetching instructions at full speed, there is plenty of time for the CoreSight debug module to read or write to memory without stealing any CPU cycles. This can be slightly intrusive in the unlikely event the CPU and μVision reads or writes to the same memory location at exactly the same time. Then the CPU will be stalled for one clock cycle. In practice, this use of additional CPU cycles never happens.
provides the ability to view certain registers of peripherals and the CPU core. In many cases, these windows are updated in real-time while your program is running. They are available only in debug mode. Go to Peripherals - System Viewer to open the peripheral windows. Select CPU core registers by going to Peripherals - Core Peripherals.
The example application uses the Arm Cortex-M system tick timer.
Go to Peripherals - Core Peripherals and then select System Tick Timer S (SysTick).
- The SysTick Timer window opens:
Run (F5) the application.
- While the program is running, type
0x10000 in the
SysTick -> LOAD register and click in another register or press Enter.
- The program execution will speed up. This is the power of Arm CoreSight debugging.
- Replace LOAD with 0x007CFF. A
Reset will also accomplish this.
- When you are done,
Stop the application and close the SysTick Tick Timer (SysTick) window.
Watchpoints can be thought of as conditional (access) breakpoints. Most Armv7-M and Armv8-M based processors have four data comparators. Since each watchpoint uses two comparators, you can configure two of them. The Logic Analyzer uses the same comparators in its operations. This means in μVision you must have two variables free in the Logic Analyzer to use watchpoints. μVision supports only one watchpoint.Note
The following does not work with the FVP used in this example. Switch to a real hardware target to use watchpoints.
Configure a Watchpoint
- Go to Debug - Breakpoints (Ctrl-B) to open the Breakpoints window.
- In the Expression box, enter g_msTicks==0x44.
- Select both the Read and Write Access.
- Click on Define and it will be accepted as shown here:
- Click on Close.
Run (F5) the application.
0x44, the program will halt.
- You will see
g_msTicks displayed with a value of 0x44 in the Watch window:
- Delete the watchpoint by selecting Kill All in the Breakpoints window or use
Kill all Breakpoints in Current Target.
- Select Close.
- Leave Debug mode.
Use a Watchpoint in the Stack Space
If you put a RAM address as the expression with no value, the next read and/or write (as you selected) will cause the program to halt. This can be particularly useful in the Stack. Set an address at some limit and if the program reads or writes this address, the program stops.
- In this example a watchpoint is created with address
- Running the program, the first read or write will stop the processor.
window shows the setting of this Watchpoint and its execution including the approximate instruction location:
- Skid: The instruction noted will not be the instruction causing the break as it has not been executed yet. It will be noted in Disassembly.
- The instruction causing the break is probably a few instructions before this one in the execution stream.
- To edit a watchpoint, double-click on it in the Breakpoints window and its information will be dropped down into the configuration area. Clicking on Define will create another watchpoint. You should delete the old one by highlighting it and click on Kill Selected or use the following tip.
- The checkbox beside the expression in Current Breakpoints: as shown above allows you to temporarily unselect or disable a watchpoint without deleting it.
- You can create a watchpoint with a raw address and no variable value. This is useful for detecting stack overruns. Physical addresses can be entered as
*((unsigned long *)0x20000000). Or simply enter the address as shown above.