Skip to content

Commit 21a767e

Browse files
Semihosting part of core, RISC-V support (#2685)
Semihosting is handy for debugging, so allow the core to use `SerialSemi` as the ::printf port. Add menu item to the IDE to allow selection. Add RISC-V implementation of semihost call
1 parent a02e188 commit 21a767e

File tree

14 files changed

+355
-119
lines changed

14 files changed

+355
-119
lines changed

boards.txt

Lines changed: 228 additions & 0 deletions
Large diffs are not rendered by default.

cores/rp2040/Arduino.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ extern const String emptyString;
126126
#endif
127127

128128
#include "SerialUART.h"
129+
#include "SerialSemi.h"
129130
#include "RP2040Support.h"
130131
#include "SerialPIO.h"
131132
#include "Bootsel.h"

libraries/Semihosting/src/SemiFS.h renamed to cores/rp2040/SemiFS.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
#pragma once
2121

2222
#include "Semihosting.h"
23-
#include <FS.h>
24-
#include <FSImpl.h>
23+
#include "FS.h"
24+
#include "FSImpl.h"
2525

2626
using namespace fs;
2727

@@ -57,7 +57,7 @@ class SemiFSFileImpl : public FileImpl {
5757
a[0] = _fd;
5858
a[1] = (uint32_t)buf;
5959
a[2] = size;
60-
return 0 == Semihost(SYS_WRITE, a) ? size : -1;
60+
return 0 == Semihost(SEMIHOST_SYS_WRITE, a) ? size : -1;
6161
}
6262
return -1; // some kind of error
6363
}
@@ -68,7 +68,7 @@ class SemiFSFileImpl : public FileImpl {
6868
a[0] = _fd;
6969
a[1] = (uint32_t)buf;
7070
a[2] = size;
71-
int ret = Semihost(SYS_READ, a);
71+
int ret = Semihost(SEMIHOST_SYS_READ, a);
7272
if (ret == 0) {
7373
return size;
7474
} else if (ret == (int)size) {
@@ -92,7 +92,7 @@ class SemiFSFileImpl : public FileImpl {
9292
uint32_t a[2];
9393
a[0] = _fd;
9494
a[1] = pos;
95-
return !Semihost(SYS_SEEK, a);
95+
return !Semihost(SEMIHOST_SYS_SEEK, a);
9696
}
9797

9898
size_t position() const override {
@@ -105,7 +105,7 @@ class SemiFSFileImpl : public FileImpl {
105105
}
106106
uint32_t a;
107107
a = _fd;
108-
int ret = Semihost(SYS_FLEN, &a);
108+
int ret = Semihost(SEMIHOST_SYS_FLEN, &a);
109109
if (ret < 0) {
110110
return 0;
111111
}
@@ -119,7 +119,7 @@ class SemiFSFileImpl : public FileImpl {
119119
void close() override {
120120
if (_opened) {
121121
uint32_t a = _fd;
122-
Semihost(SYS_CLOSE, &a);
122+
Semihost(SEMIHOST_SYS_CLOSE, &a);
123123
_opened = false;
124124
}
125125
}
@@ -202,7 +202,7 @@ class SemiFSImpl : public FSImpl {
202202
a[0] = (uint32_t)path;
203203
a[1] = mode;
204204
a[2] = strlen(path);
205-
int handle = Semihost(SYS_OPEN, a);
205+
int handle = Semihost(SEMIHOST_SYS_OPEN, a);
206206
if (handle < 0) {
207207
return FileImplPtr();
208208
}
@@ -225,7 +225,7 @@ class SemiFSImpl : public FSImpl {
225225
a[1] = strlen(pathFrom);
226226
a[2] = (uint32_t)pathTo;
227227
a[3] = strlen(pathTo);
228-
return !Semihost(SYS_RENAME, a);
228+
return !Semihost(SEMIHOST_SYS_RENAME, a);
229229
}
230230

231231
bool info(FSInfo& info) override {
@@ -237,7 +237,7 @@ class SemiFSImpl : public FSImpl {
237237
uint32_t a[2];
238238
a[0] = (uint32_t)path;
239239
a[1] = strlen(path);
240-
return !Semihost(SYS_REMOVE, a);
240+
return !Semihost(SEMIHOST_SYS_REMOVE, a);
241241
}
242242

243243
bool mkdir(const char* path) override {
@@ -258,15 +258,15 @@ class SemiFSImpl : public FSImpl {
258258
a[0] = (uint32_t)path;
259259
a[1] = 0; // READ
260260
a[2] = strlen(path);
261-
int fn = Semihost(SYS_OPEN, a);
261+
int fn = Semihost(SEMIHOST_SYS_OPEN, a);
262262
if (fn < 0) {
263263
return false;
264264
}
265265
bzero(st, sizeof(*st));
266266
a[0] = fn;
267-
st->size = Semihost(SYS_FLEN, a);
267+
st->size = Semihost(SEMIHOST_SYS_FLEN, a);
268268
a[0] = fn;
269-
Semihost(SYS_CLOSE, a);
269+
Semihost(SEMIHOST_SYS_CLOSE, a);
270270
return true;
271271
}
272272

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include "Semihosting.h"
2+
#include "SerialSemi.h"
3+
#include "SemiFS.h"
24

35
SerialSemiClass SerialSemi;
46
FS SemiFS = FS(FSImplPtr(new semifs::SemiFSImpl()));

cores/rp2040/Semihosting.h

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
Semihosting.h - Semihosting for Serial and FS access via GDB
3+
Copyright (c) 2024 Earle F. Philhower, III. All rights reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
#pragma once
21+
22+
// Be sure to only use this library with GDB and to enable the ARM semihosting support
23+
// (gdb) monitor arm semihosting enable
24+
25+
// Input/output will be handled by OpenOCD
26+
27+
// From https://developer.arm.com/documentation/dui0471/g/Semihosting/Semihosting-operations?lang=en
28+
typedef enum {
29+
SEMIHOST_SYS_CLOSE = 0x02,
30+
SEMIHOST_SYS_CLOCK = 0x10,
31+
SEMIHOST_SYS_ELAPSED = 0x30,
32+
SEMIHOST_SYS_ERRNO = 0x13,
33+
SEMIHOST_SYS_FLEN = 0x0C,
34+
SEMIHOST_SYS_GET_CMDLINE = 0x15,
35+
SEMIHOST_SYS_HEAPINFO = 0x16,
36+
SEMIHOST_SYS_ISERROR = 0x08,
37+
SEMIHOST_SYS_ISTTY = 0x09,
38+
SEMIHOST_SYS_OPEN = 0x01,
39+
SEMIHOST_SYS_READ = 0x06,
40+
SEMIHOST_SYS_READC = 0x07,
41+
SEMIHOST_SYS_REMOVE = 0x0E,
42+
SEMIHOST_SYS_RENAME = 0x0F,
43+
SEMIHOST_SYS_SEEK = 0x0A,
44+
SEMIHOST_SYS_SYSTEM = 0x12,
45+
SEMIHOST_SYS_TICKFREQ = 0x31,
46+
SEMIHOST_SYS_TIME = 0x11,
47+
SEMIHOST_SYS_TMPNAM = 0x0D,
48+
SEMIHOST_SYS_WRITE = 0x05,
49+
SEMIHOST_SYS_WRITEC = 0x03,
50+
SEMIHOST_SYS_WRITE0 = 0x04
51+
} SEMIHOST_OPCODES;
52+
53+
#ifdef __arm__
54+
55+
// From https://github.com/ErichStyger/mcuoneclipse/blob/master/Examples/MCUXpresso/FRDM-K22F/FRDM-K22F_Semihosting/source/McuSemihost.c
56+
static inline int __attribute__((always_inline)) Semihost(int reason, void *arg) {
57+
int value;
58+
__asm volatile(
59+
"mov r0, %[rsn] \n" /* place semihost operation code into R0 */
60+
"mov r1, %[arg] \n" /* R1 points to the argument array */
61+
"bkpt 0xAB \n" /* call debugger */
62+
"mov %[val], r0 \n" /* debugger has stored result code in R0 */
63+
64+
: [val] "=r"(value) /* outputs */
65+
: [rsn] "r"(reason), [arg] "r"(arg) /* inputs */
66+
: "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc" /* clobber */
67+
);
68+
return value; /* return result code, stored in R0 */
69+
}
70+
#else
71+
72+
// https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/n-5VQ9PHZ4w/m/KbzH5t9MBgAJ
73+
static inline int __attribute__((always_inline)) Semihost(int reason, void *argPack) {
74+
register int value asm("a0") = reason;
75+
register void *ptr asm("a1") = argPack;
76+
asm volatile(
77+
// Force 16-byte alignment to make sure that the 3 instructions fall
78+
// within the same virtual page.
79+
" .balign 16 \n"
80+
" .option push \n"
81+
// Force non-compressed RISC-V instructions
82+
" .option norvc \n"
83+
// semihosting e-break sequence
84+
" slli x0, x0, 0x1f \n" // # Entry NOP
85+
" ebreak \n" // # Break to debugger
86+
" srai x0, x0, 0x7 \n" // # NOP encoding the semihosting call number 7
87+
" .option pop \n"
88+
/*mark (value) as an output operand*/
89+
: "=r"(value) /* Outputs */
90+
// The semihosting call number is passed in a0, and the argument in a1.
91+
: "0"(value), "r"(ptr) /* Inputs */
92+
// The "memory" clobber makes GCC assume that any memory may be arbitrarily read or written by the asm block,
93+
// so will prevent the compiler from reordering loads or stores across it, or from caching memory values in registers across it.
94+
// The "memory" clobber also prevents the compiler from removing the asm block as dead code.
95+
: "memory" /* Clobbers */
96+
);
97+
return value;
98+
}
99+
#endif

libraries/Semihosting/src/SerialSemi.h renamed to cores/rp2040/SerialSemi.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
#include "Semihosting.h"
2323

24-
#include <Arduino.h>
24+
#include "Arduino.h"
2525
#include "api/HardwareSerial.h"
2626

2727
class SerialSemiClass : public HardwareSerial {
@@ -61,7 +61,7 @@ class SerialSemiClass : public HardwareSerial {
6161
_peeked = false;
6262
return _peekedChar;
6363
}
64-
return Semihost(SYS_READC, nullptr);
64+
return Semihost(SEMIHOST_SYS_READC, nullptr);
6565
}
6666

6767
virtual int available() override {
@@ -80,7 +80,7 @@ class SerialSemiClass : public HardwareSerial {
8080

8181
virtual size_t write(uint8_t c) override {
8282
int32_t param = c;
83-
Semihost(SYS_WRITEC, &param);
83+
Semihost(SEMIHOST_SYS_WRITEC, &param);
8484
return 1;
8585
}
8686

docs/semihosting.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ Simply include ``<Semihosting.h>`` in your application and use ``SerialSemi`` as
4040
* Baud rate, bit width, etc. are all ignored
4141
* Input is limited because ``read`` may hang indefinitely in the host and ``available`` is not part of the spec
4242

43+
``SerialSemi`` can also be selected as the debug output port in the IDE, in which case ``::printf`` will write
44+
to the debugger directly.
45+
4346
SemiFS - Host filesystem access through Semihosting
4447
---------------------------------------------------
4548

keywords.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ digitalReadFast KEYWORD2
8686

8787
enableDoubleResetBootloader KEYWORD2
8888

89+
SerialSemi KEYWORD2
90+
SemiFS KEYWORD2
91+
8992
openDir KEYWORD2
9093
next KEYWORD2
9194
getLastWrite KEYWORD2

libraries/Semihosting/keywords.txt

Lines changed: 0 additions & 18 deletions
This file was deleted.

libraries/Semihosting/library.properties

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)