Skip to content

Commit 3c08cb7

Browse files
committed
Book: Describe GDB-RSP interface in BMFlash (for capturing semihosting in Tcl scripts); move semihosting to a separate section, and expand the coverage.
Update semihosting source example. General GUI utilities: switch to C11 threads (with emulation layer, if needed); drop picoro. BMDebug: fix related to changed response messages in recent GDB; improvements in support for semihosting; ability to break out of GDB loop if BMP looses connection to the target. BMFlash: support GDB-RSP in Tcl script, handle a subset of semihosting requests; ability to abort a Tcl script (e.g. if it drops into an endless loop).
1 parent 678e515 commit 3c08cb7

30 files changed

+3187
-919
lines changed

‎BlackMagicProbe.pdf‎

21.3 KB
Binary file not shown.

‎doc/blackmagicprobe-book.jpg‎

-22.2 KB
Loading

‎examples/semihosting.c‎

Lines changed: 155 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
/* Implementation of a simple semihosting interface (output only) for ARM Cortex
2-
* micro-controllers. This implementation is based on CMSIS, but it is easily
3-
* adapted to libopencm3 or other micro-controller support libraries.
2+
* micro-controllers. This implementation is based on CMSIS, and restricted to
3+
* the semihosting calls supported by the Black Magic Probe. It is easily
4+
* adapted to libopencm3 or other micro-controller support libraries. Likewise,
5+
* it is easily extended with the few semihosting calls that the Black Magic
6+
* Probe does not support.
47
*
5-
* Copyright 2020 CompuPhase
8+
* Copyright 2020-2023 CompuPhase
69
*
710
* This software is provided 'as-is', without any express or implied
811
* warranty. In no event will the authors be held liable for any damages
@@ -51,27 +54,30 @@ void HardFault_Handler(void)
5154
"ignore:\n"
5255
"add r1, #2\n" /* skip behind BKPT 0xAB */
5356
"str r1, [r0,#24]\n" /* store this value on the stack */
57+
"mov r0, #0 \n" /* set error code (r0 == -1) */
58+
"sub r0, #1 \n"
5459
"bx lr"
5560
);
5661
}
5762
#endif
5863

59-
void host_puts(int file, const char *text)
64+
__attribute__((naked))
65+
int semihosting(uint32_t command, void *params)
6066
{
6167
#if defined __ARM_ARCH && __ARM_ARCH == 7
62-
/* for Cortex M3/M4, test whether we are running under a debugger */
63-
#define CoreDebug_DHCSR *((uint32_t*)0xE000EDF0UL)
68+
/* for Cortex M3/M4, test whether we are running under a debugger (for
69+
Cortex M0(+), the HardFault exception handler intercepts the call) */
70+
# define CoreDebug_DHCSR *((uint32_t*)0xE000EDF0UL)
6471
if (CoreDebug_DHCSR & 1) {
6572
#endif
6673

67-
uint32_t command = 5; /*SYS_WRITE*/
68-
uint32_t packet[3] = { file, (uint32_t)text, strlen(text) };
6974
__asm__ (
70-
"mov r0, %0\n"
71-
"mov r1, %1\n"
72-
"bkpt #0xAB\n"
75+
"mov r0, %0 \n"
76+
"mov r1, %1 \n"
77+
"bkpt #0xAB \n"
78+
"bx lr \n"
7379
:
74-
: "r" (command), "r" (packet)
80+
: "r" (command), "r" (params)
7581
: "r0", "r1", "memory"
7682
);
7783

@@ -80,3 +86,140 @@ void host_puts(int file, const char *text)
8086
#endif
8187
}
8288

89+
int sys_open(const char *path, const char *mode)
90+
{
91+
uint32_t params[3] = { (uint32_t)path, 0, strlen(path) };
92+
if (*mode == 'r') {
93+
mode++;
94+
} else if (*mode == 'w') {
95+
params[1] |= 0x04;
96+
mode++;
97+
} else if (*mode == 'a') {
98+
params[1] |= 0x08;
99+
mode++;
100+
}
101+
if (*mode == '+') {
102+
params[1] |= 0x02;
103+
mode++;
104+
}
105+
if (*mode == 'b') {
106+
params[1] |= 0x01;
107+
mode++;
108+
}
109+
return semihosting(SYS_OPEN, params);
110+
}
111+
112+
int sys_close(int fd)
113+
{
114+
return semihosting(SYS_CLOSE, &fd);
115+
}
116+
117+
void sys_writec(char c)
118+
{
119+
semihosting(SYS_WRITEC, &c);
120+
}
121+
122+
void sys_write0(const char *text)
123+
{
124+
semihosting(SYS_WRITE0, (char*)text);
125+
}
126+
127+
int sys_write(int fd, const unsigned char *buffer, size_t size)
128+
{
129+
uint32_t params[3] = { fd, (uint32_t)buffer, size };
130+
return semihosting(SYS_WRITE, params);
131+
}
132+
133+
int sys_read(int fd, char *buffer, size_t size)
134+
{
135+
uint32_t params[3] = { fd, (uint32_t)buffer, size };
136+
return semihosting(SYS_READ, params);
137+
}
138+
139+
int sys_readc(void)
140+
{
141+
return semihosting(SYS_READC, NULL);
142+
}
143+
144+
int sys_iserror(uint32_t code)
145+
{
146+
return semihosting(SYS_ISERROR, &code);
147+
}
148+
149+
int sys_istty(int fd)
150+
{
151+
return semihosting(SYS_ISTTY, &fd);
152+
}
153+
154+
int sys_seek(int fd, size_t offset)
155+
{
156+
uint32_t params[2] = { fd, offset };
157+
return semihosting(SYS_SEEK, params);
158+
}
159+
160+
int sys_flen(int fd)
161+
{
162+
return semihosting(SYS_FLEN, &fd);
163+
}
164+
165+
int sys_tmpnam(int id, char *buffer, size_t size)
166+
{
167+
uint32_t params[3] = { (uint32_t)buffer, (id & 0xff), size };
168+
return semihosting(SYS_TMPNAM, params);
169+
}
170+
171+
int sys_remove(const char *path)
172+
{
173+
uint32_t params[2] = { (uint32_t)path, strlen(path) };
174+
return semihosting(SYS_REMOVE, params);
175+
}
176+
177+
int sys_rename(const char *from, const char *to)
178+
{
179+
uint32_t params[4] = { (uint32_t)from, strlen(from), (uint32_t)to, strlen(to) };
180+
return semihosting(SYS_RENAME, params);
181+
}
182+
183+
int sys_clock(void)
184+
{
185+
return semihosting(SYS_CLOCK, NULL);
186+
}
187+
188+
int sys_time(void)
189+
{
190+
return semihosting(SYS_TIME, NULL);
191+
}
192+
193+
int sys_system(const char *command)
194+
{
195+
uint32_t params[2] = { (uint32_t)command, strlen(command) };
196+
return semihosting(SYS_SYSTEM, params);
197+
}
198+
199+
int sys_errno(void)
200+
{
201+
return semihosting(SYS_ERRNO, NULL);
202+
}
203+
204+
void sys_get_cmdline(char *buffer, size_t size)
205+
{
206+
uint32_t params[2] = { (uint32_t)buffer, size };
207+
semihosting(SYS_GET_CMDLINE, params);
208+
}
209+
210+
void sys_heapinfo(struct heapinfo *info)
211+
{
212+
semihosting(SYS_HEAPINFO, &info);
213+
}
214+
215+
void sys_exit(int trap)
216+
{
217+
semihosting(SYS_EXIT, (void*)trap);
218+
}
219+
220+
void sys_exit_extended(int trap, int subcode)
221+
{
222+
uint32_t params[2] = { trap, subcode };
223+
semihosting(SYS_EXIT_EXTENDED, params);
224+
}
225+

‎examples/semihosting.h‎

Lines changed: 175 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
/* Implementation of a simple semihosting interface (output only) for ARM Cortex
2-
* micro-controllers. This implementation is based on CMSIS, but it is easily
3-
* adapted to libopencm3 or other micro-controller support libraries.
2+
* micro-controllers. This implementation is based on CMSIS, and restricted to
3+
* the semihosting calls supported by the Black Magic Probe. It is easily
4+
* adapted to libopencm3 or other micro-controller support libraries. Likewise,
5+
* it is easily extended with the few semihosting calls that the Black Magic
6+
* Probe does not support.
47
*
5-
* Copyright 2020 CompuPhase
8+
* Copyright 2020-2023 CompuPhase
69
*
710
* This software is provided 'as-is', without any express or implied
811
* warranty. In no event will the authors be held liable for any damages
@@ -24,13 +27,180 @@
2427
#ifndef __SEMIHOSTING_H
2528
#define __SEMIHOSTING_H
2629

30+
#define SYS_OPEN 0x01
31+
#define SYS_CLOSE 0x02
32+
#define SYS_WRITEC 0x03
33+
#define SYS_WRITE0 0x04
34+
#define SYS_WRITE 0x05
35+
#define SYS_READ 0x06
36+
#define SYS_READC 0x07
37+
#define SYS_ISERROR 0x08
38+
#define SYS_ISTTY 0x09
39+
#define SYS_SEEK 0x0A
40+
#define SYS_FLEN 0x0C
41+
#define SYS_TMPNAM 0x0D
42+
#define SYS_REMOVE 0x0E
43+
#define SYS_RENAME 0x0F
44+
#define SYS_CLOCK 0x10
45+
#define SYS_TIME 0x11
46+
#define SYS_SYSTEM 0x12
47+
#define SYS_ERRNO 0x13
48+
#define SYS_GET_CMDLINE 0x15
49+
#define SYS_HEAPINFO 0x16
50+
#define SYS_EXIT 0x18
51+
#define SYS_EXIT_EXTENDED 0x20
52+
2753
#define STDOUT 1
2854
#define STDERR 2
2955

30-
/** host_puts() sends a string to the debugger console via semihosting. The
56+
struct heapinfo {
57+
void *heap_base;
58+
void *heap_limit;
59+
void *stack_base;
60+
void *stack_limit;
61+
};
62+
63+
#include <stddef.h>
64+
65+
/** sys_open() opens a file on the host. The mode is one of:
66+
* - "r", "rb", "r+", "r+b"
67+
* - "w", "wb", "w+", "w+b"
68+
* - "a", "ab", "a+", "a+b"
69+
*
70+
* \return A file handle.
71+
*/
72+
int sys_open(const char *path, const char *mode);
73+
74+
/** sys_close() closes a file.
75+
*
76+
* \return 0 on success, -1 on failure.
77+
*/
78+
int sys_close(int fd);
79+
80+
/** sys_writec() writes a single character to the debugger console.
81+
*/
82+
void sys_writec(char c);
83+
84+
/** sys_write0() sends a zero-terminated string to the debugger console. The
3185
* string is transmitted as is, no newline is appended.
3286
*/
33-
void host_puts(int file, const char *text);
87+
void sys_write0(const char *text);
88+
89+
/** sys_write() writes a data buffer to a file. The file handle can be the
90+
* pre-defined handles 1 or 2 (for stdio and stderr respectively), or a
91+
* handle previously opened with sys_open().
92+
*
93+
* \return The number of bytes *not* written. Hence, on success, the function
94+
* returns 0.
95+
*/
96+
int sys_write(int fd, const unsigned char *buffer, size_t size);
97+
98+
/** sys_read() reads data from a file opened on the host.
99+
*
100+
* \return The number of bytes *not* read. More specifically, the function
101+
* requests to read "size" bytes. When it successfully does so, the
102+
* function returns 0. When it reads less than "size", it returns
103+
* the number of bytes that it comes short of "size"
104+
*/
105+
int sys_read(int fd, char *buffer, size_t size);
106+
107+
/** sys_readc() reads a character from the console (stdin).
108+
*
109+
* \return The character read.
110+
*/
111+
int sys_readc(void);
112+
113+
/** sys_iserror() returns whether the code in the parameter is an error code
114+
* (or whether it is a normal return code).
115+
*/
116+
int sys_iserror(uint32_t code);
117+
118+
/** sys_istty() returns whether the file handle is a TTY device. GDB defines
119+
* only stdin, stdout & stderr as TTY devices.
120+
*/
121+
int sys_istty(int fd);
122+
123+
/** sys_seek() sets the read or write position in a file. The position is
124+
* relative to the beginning of the file.
125+
*
126+
* \return 0 on success, -1 or error.
127+
*/
128+
int sys_seek(int fd, size_t offset);
129+
130+
/** sys_flen() returns the length of a file in bytes (returns -1 on error).
131+
*/
132+
int sys_flen(int fd);
133+
134+
/** sys_tmpnam() returns a name for a temporary file. The "id" parameter must
135+
* be between 0 and 255. It is up to the implementation whether the "id"
136+
* becomes part of the filename (or whether it is completely ignored).
137+
*
138+
* \return 0 on success, -1 on failure.
139+
*/
140+
int sys_tmpnam(int id, char *buffer, size_t size);
141+
142+
/** sys_remove() deletes a file on the host.
143+
*
144+
* \return 0 on success, an host-specific error code (errno) on failure.
145+
*/
146+
int sys_remove(const char *path);
147+
148+
/** sys_rename() renames a file on the host.
149+
*
150+
* \return 0 on success, an host-specific error code (errno) on failure.
151+
*/
152+
int sys_rename(const char *from, const char *to);
153+
154+
/** sys_clock() returns execution time in hundredths of a second (centisecond).
155+
* Note that with this call, the target asks the debugger how long it has been
156+
* running.
157+
*
158+
* \return The number of centiseconds passed, or -1 in case of an error.
159+
*/
160+
int sys_clock(void);
161+
162+
/** sys_time() returns the current time ("real world time") in the form of the
163+
* number of seconds since the Unix Epoch.
164+
*
165+
* \return The number of seconds since 00:00:00 January 1, 1970.
166+
*/
167+
int sys_time(void);
168+
169+
/** sys_system() executes the command in a command shell on the host.
170+
*
171+
* \return The exit code of the command.
172+
*/
173+
int sys_system(const char *command);
174+
175+
/** sys_errno() returns the error code of the previous command that sets it:
176+
* sys_open(), sys_close(), sys_read(), sys_write(), sys_seek()m sys_remove(),
177+
* sys_rename().
178+
*
179+
* \return The "errno" value.
180+
*/
181+
int sys_errno(void);
182+
183+
/** sys_get_cmdline() returns the parameters passed to the target on a "start"
184+
* or "run" command.
185+
*/
186+
void sys_get_cmdline(char *buffer, size_t size);
187+
188+
/** sys_heapinfo() retrieves the top & bottom addresses of the stack and the
189+
* heap.
190+
*/
191+
void sys_heapinfo(struct heapinfo *info);
192+
193+
/** sys_exit() signals the host that the target has dropping into an exception
194+
* trap (where areaching the end of the application, is also considered an
195+
* exception).
196+
*/
197+
void sys_exit(int trap);
198+
199+
/** sys_exit_extended() signals the host that the target has dropping into an
200+
* exception trap (where areaching the end of the application, is also
201+
* considered an exception).
202+
*/
203+
void sys_exit_extended(int trap, int subcode);
34204

35205
#endif /* __SEMIHOSTING_H */
36206

0 commit comments

Comments
 (0)