cortex-m0+ SWD debugging on the cheap

STM32 Discovery Board connected to an LPC812 on a breadboard
STM32 Discovery Board connected to an LPC812 on a breadboard

I’m fairly confident you aren’t likely to find out how to use an STM32F0-Discovery board to upload and debug an LPC812 chip from either the ST or the NXP forum websites. However, if you read this post I will walk you though the steps.

So what is this post all about?  SWD stands for Serial Wire Debugging.  It is a debugging technology built into the arm cortex-m0+ cores. Using just two wires, it allows true hardware debugging of the LPC812 chip.  Hardware debugging lets you step line by line through your program as it executes on the chip. Memory can be examined along with registers and variables in the debugger to help you figure out what is going one when your program isn’t doing exactly what you expected. Setting breakpoints lets you stop at specific lines of code allowing you to wait for a specific condition to be executed.  You can also check for things like stack overflow and memory out of bounds access. Once you get used to hardware debugging, you will wonder how you got along without it.

So let’s step back a bit and review at how one goes about getting a compiled program bits into the flash of an ARM chip.  The LPC812 has a couple of methods. First up is the built-in bootloader. It can take your intel hex files and write them to flash. This is handled over a UART connection, usually with a USB to UART dongle.  Two popular programs that use the bootloader protocol are flashmagic and lpc21isp.  The downside of using a bootloader is that it just loads files, it doesn’t offer any debugging features.

The second method is to use a hardware JTAG debugger. Using a JTAG device, we can both load the flash memory and then debug it.  A popular option for the NXP chips is the NXP LPCXpresso with its LPCLink debug hardware. The downside with the LPCXpresso boards is mainly the price, they are somewhat expensive. However another option is to use the STLink-V2 SWD programmer built in to the STM32 discovery boards which can be had for about $10.  We are cheap so we are going to figure out how to use the STLink-V2 with the openocd software.

Openocd expects you to provide configuration files (ending in .cfg) that describe your debugging interface hardware and the target chip you are using.  I’ve created some openocd configuration files for the stlink-v2 interface working with an nxp lpc812 target chip. When setting up a chip, you describe the flash and ram resources available on the chip and which openocd flash write routines are used.  In this post, I’m not going to go into details of all the configuration settings.  However, you can find the lpc812 with stlink-v2 openocd .cfg files here on my gist:

https://gist.github.com/RickKimball/9009480

Clone that repository and copy the .cfg files to the directory your lpc812 source code.  Compile your program and then use the instructions below to load openocd and then gdb to load the code.  I’m testing with a simple blink program.

Here I launch two windows, one running the openocd server and the other running the arm-none-eabi-gdb client that connects to openocd. In the first window we launch openocd and provide the debug812.cfg file:

$ openocd -s . -f debug812.cfg
Open On-Chip Debugger 0.8.0-dev-00331-g1137eae (2014-01-25-15:17)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.sourceforge.net/doc/doxygen/bugs.html
Info : This adapter doesn't support configurable speed
Info : STLINK v2 JTAG v17 API v2 SWIM v0 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 2.902193
Info : lpc812.cpu: hardware has 4 breakpoints, 2 watchpoints
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x1fff0008 msp: 0x10000ffc
Info : accepting 'gdb' connection from 3333
Warn : acknowledgment received, but no packet pending
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x1fff0008 msp: 0x10000ffc
target state: halted
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x10000004 msp: 0x100000c8
Warn : Verification will fail since checksum in image (0x00000000) to be written to flash is different from calculated vector checksum (0xeffff9b1).
Warn : To remove this warning modify build tools on developer PC to inject correct LPC vector checksum.
target state: halted
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x10000004 msp: 0x100000c8
Info : halted: PC: 0x000000cc
Info : halted: PC: 0x00000164
Info : halted: PC: 0x00000166
Info : halted: PC: 0x00000168
Info : halted: PC: 0x0000016a
Info : halted: PC: 0x0000016c
Info : halted: PC: 0x0000016e
Info : halted: PC: 0x00000170
Info : halted: PC: 0x00000172
Info : halted: PC: 0x00000174
Info : halted: PC: 0x00000176
Info : halted: PC: 0x00000178
Info : halted: PC: 0x0000017a
Info : halted: PC: 0x0000017c
Info : halted: PC: 0x0000017e
Info : halted: PC: 0x00000180
Info : halted: PC: 0x00000182
Info : halted: PC: 0x00000184
Info : halted: PC: 0x00000186
Info : halted: PC: 0x00000188
Info : halted: PC: 0x0000018a
Info : halted: PC: 0x0000019e
Info : halted: PC: 0x0000019e

 

In the second window, I launch the arm gdb client and connect to the openocd gdb server on localhost using port 3333.  The next step is to load your code into flash using the gdb ‘load’ command. In my case, my program is .bin/template.elf. You should replace that filename with your own lpc812 specific firmware. In the session below you can see me using gdb commands to reset, start running the program, setting breakpoints and then continuing:

$ arm-none-eabi-gdb -ex 'target extended-remote :3333' -ex 'monitor reset init' .bin/template.elf 
GNU gdb (GNU Tools for ARM Embedded Processors) 7.4.1.20130913-cvs
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-linux-gnu --target=arm-none-eabi".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/kimballr/github/lpc8xx/.bin/template.elf...done.
Remote debugging using :3333
0x1fff0008 in ?? ()
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x1fff0008 msp: 0x10000ffc
(gdb) load
Loading section .text, size 0x1d0 lma 0x0
Start address 0xcb, load size 464
Transfer rate: 2 KB/sec, 464 bytes/write.
(gdb) b ResetISR 
Breakpoint 1 at 0xca: file core/startup_lpc8xx.c, line 193.
(gdb) c
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, ResetISR () at core/startup_lpc8xx.c:193
193		while (dst < &_edata)
(gdb) n
186	ResetISR(void)
(gdb) b main
Breakpoint 2 at 0x162: file main.c, line 35.
(gdb) c
Continuing.

Breakpoint 2, main () at main.c:35
35		LPC_FLASHCTRL->FLASHCFG &= ~(0b11);                    // configure flash for 0 wait state
(gdb) n
36		SysTick_Config(12000000 / 1000);          // 12Mhz / 1000
(gdb) n
38		LPC_GPIO_PORT->DIR0 |= LED_MASK;       // set pin as output
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
delay_ms (ms=ms@entry=100) at main.c:28
28	    while ((msecs - now) < ms) {
(gdb) where
#0  delay_ms (ms=ms@entry=100) at main.c:28
#1  0x000001bc in main () at main.c:46
(gdb) l
23	
24	void delay_ms(uint32_t ms)
25	{
26	    uint32_t now = msecs;
27	
28	    while ((msecs - now) < ms) {
29	    	__WFI();
30	    }
31	}
32	
(gdb) l
33	int main(void)
34	{
35		LPC_FLASHCTRL->FLASHCFG &= ~(0b11);                    // configure flash for 0 wait state
36		SysTick_Config(12000000 / 1000);          // 12Mhz / 1000
37	
38		LPC_GPIO_PORT->DIR0 |= LED_MASK;       // set pin as output
39	
40		//loop();
41	
42		while (1) {
(gdb) 
43			LPC_GPIO_PORT->SET0 = LED_MASK;    // LED output high
44			delay_ms(100);
45			LPC_GPIO_PORT->CLR0 = LED_MASK;    // LED output low
46			delay_ms(100);
47		}
48	}
49	
50	#if 1
51	/*
52	 * using single cycle GPIO in asm
(gdb) b 43
Breakpoint 3 at 0x19c: file main.c, line 43.
(gdb) c
Continuing.

Breakpoint 3, main () at main.c:43
43			LPC_GPIO_PORT->SET0 = LED_MASK;    // LED output high
(gdb) c
Continuing.

Breakpoint 3, main () at main.c:43
43			LPC_GPIO_PORT->SET0 = LED_MASK;    // LED output high
(gdb) c
Continuing.

Breakpoint 3, main () at main.c:43
43			LPC_GPIO_PORT->SET0 = LED_MASK;    // LED output high
(gdb) c
Continuing.

Breakpoint 3, main () at main.c:43
43			LPC_GPIO_PORT->SET0 = LED_MASK;    // LED output high
(gdb)

stlinkv2_lpc812_01

Leave a Reply