mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
stdio_semihosting: Initial include of Semihosting-based STDIO
This commit is contained in:
parent
aff18b9599
commit
99ca736b53
@ -4,6 +4,7 @@ STDIO_MODULES = \
|
||||
stdio_ethos \
|
||||
stdio_null \
|
||||
stdio_rtt \
|
||||
stdio_semihosting \
|
||||
stdio_uart \
|
||||
#
|
||||
|
||||
@ -49,3 +50,8 @@ ifeq (,$(filter stdio_cdc_acm,$(USEMODULE)))
|
||||
FEATURES_BLACKLIST += bootloader_arduino
|
||||
FEATURES_BLACKLIST += bootloader_nrfutil
|
||||
endif
|
||||
|
||||
ifneq (,$(filter stdio_semihosting,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
FEATURES_REQUIRED += arch_cortexm
|
||||
endif
|
||||
|
@ -33,6 +33,5 @@ else ifeq ($(RIOT_TERMINAL),semihosting)
|
||||
TERMPROG = $(DEBUGGER)
|
||||
TERMFLAGS = $(DEBUGGER_FLAGS)
|
||||
OPENOCD_DBG_EXTRA_CMD += -c 'arm semihosting enable'
|
||||
OPENOCD_DBG_EXTRA_CMD += -c 'set remotetimeout 10000'
|
||||
$(call target-export-variables,term cleanterm,OPENOCD_DBG_EXTRA_CMD)
|
||||
endif
|
||||
|
79
sys/include/stdio_semihosting.h
Normal file
79
sys/include/stdio_semihosting.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Koen Zandberg
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_stdio_semihosting STDIO over Semihosting
|
||||
* @ingroup sys
|
||||
*
|
||||
* @brief Standard input/output backend using ARM Semihosting
|
||||
*
|
||||
* The ARM Semihosting provides an STDIO backend using the ARM Semihosting
|
||||
* protocol. The main advantage of Semihosting is that is allows STDIO over the
|
||||
* SWD/JTAG debugging interface already available on ARM microcontrollers.
|
||||
*
|
||||
* ARM Semihosting works by using the breakpoint instructing to trigger the
|
||||
* debugger to read the output or to write the input chars. Please be aware that
|
||||
* this might skew the timing of your application.
|
||||
*
|
||||
* The main disadvantage of Semihosting is that it is relative slow (even when
|
||||
* compared to serial uart), and that it requires an active debug session to
|
||||
* handle the breakpoint instructions. Without an active debug session the CPU
|
||||
* will halt on the first STDIO activity until the breakpoint is handled by the
|
||||
* debugger. Don't forget to disable the Semihosting module or replace it with
|
||||
* stdio_null when switching to production builds.
|
||||
*
|
||||
* As this is an ARM specific protocol, this module will only work on ARM-based
|
||||
* microcontrollers.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* Enable Semihosting-based stdio by adding the following module to your
|
||||
* makefile:
|
||||
*
|
||||
* ```
|
||||
* USEMODULE += stdio_semihosting
|
||||
* ```
|
||||
*
|
||||
* If semihosting is not the default stdio mechanism of your board, the
|
||||
* `RIOT_TERMINAL` variable has to be set to `semihosting`:
|
||||
*
|
||||
* ```
|
||||
* make term RIOT_TERMINAL=semihosting
|
||||
* ```
|
||||
*
|
||||
* Launching the terminal will start an OpenOCD session with semihosting
|
||||
* enabled. This can be used for both STDIO interaction and for debugging the
|
||||
* firmware.
|
||||
*
|
||||
* @{
|
||||
* @file
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*/
|
||||
|
||||
#ifndef STDIO_SEMIHOSTING_H
|
||||
#define STDIO_SEMIHOSTING_H
|
||||
|
||||
#include "stdio_base.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enable reception for Semihosting
|
||||
*
|
||||
* Automatically enabled when including the `stdin` module
|
||||
*/
|
||||
#define STDIO_SEMIHOSTING_RX (IS_USED(MODULE_STDIN))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/** @} */
|
||||
#endif /* STDIO_SEMIHOSTING_H */
|
1
sys/stdio_semihosting/Makefile
Normal file
1
sys/stdio_semihosting/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
137
sys/stdio_semihosting/stdio_semihosting.c
Normal file
137
sys/stdio_semihosting/stdio_semihosting.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Koen Zandberg
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup sys
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief STDIO over ARM Semihosting implementation
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "stdio_semihosting.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
#if MODULE_VFS
|
||||
#include "vfs.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Rate at which the stdin read polls (breaks) the debugger for input
|
||||
* data
|
||||
*/
|
||||
#define STDIO_SEMIHOSTING_POLL_RATE (10 * US_PER_MS)
|
||||
|
||||
/**
|
||||
* @brief ARM Semihosting STDIN file descriptor
|
||||
*/
|
||||
#define STDIO_SEMIHOSTING_F_STDIN (1)
|
||||
|
||||
/**
|
||||
* @brief ARM Semihosting STDOUT file descriptor
|
||||
*/
|
||||
#define STDIO_SEMIHOSTING_F_STDOUT (1)
|
||||
|
||||
/**
|
||||
* @name ARM Semihosting commands
|
||||
*
|
||||
* Extend when required
|
||||
* @{
|
||||
*/
|
||||
#define STDIO_SEMIHOSTING_SYS_WRITE (0x05) /**< Write command */
|
||||
#define STDIO_SEMIHOSTING_SYS_READ (0x06) /**< Read command */
|
||||
/** @} */
|
||||
|
||||
static bool _semihosting_connected(void) {
|
||||
#ifdef CoreDebug_DHCSR_C_DEBUGEN_Msk
|
||||
/* Best effort attempt to detect if a debug session is active */
|
||||
return CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t _semihosting_raw(int cmd, uint32_t *args)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
/* Moves cmd and args to r0 and r1. Then triggers a breakpoint.
|
||||
* Finally moves the results stored in r0 to result
|
||||
*/
|
||||
__asm__(
|
||||
"mov r0, %[cmd] \n"
|
||||
"mov r1, %[args] \n"
|
||||
"bkpt #0xAB \n"
|
||||
"mov %[result], r0\n"
|
||||
: /* Outputs */
|
||||
[result] "=r" (result)
|
||||
: /* Inputs */
|
||||
[cmd] "r" (cmd),
|
||||
[args] "r" (args)
|
||||
: /* Clobbered registers */
|
||||
"r0", "r1", "memory"
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t _semihosting_write(const uint8_t *buffer, size_t len)
|
||||
{
|
||||
uint32_t args[3] = {
|
||||
STDIO_SEMIHOSTING_F_STDOUT,
|
||||
(uint32_t)buffer,
|
||||
(uint32_t)len,
|
||||
};
|
||||
return _semihosting_raw(STDIO_SEMIHOSTING_SYS_WRITE, args);
|
||||
}
|
||||
|
||||
static ssize_t _semihosting_read(uint8_t *buffer, size_t len)
|
||||
{
|
||||
uint32_t args[3] = {
|
||||
STDIO_SEMIHOSTING_F_STDIN,
|
||||
(uint32_t)buffer,
|
||||
(uint32_t)len,
|
||||
};
|
||||
size_t remaining = _semihosting_raw(STDIO_SEMIHOSTING_SYS_READ, args);
|
||||
return len - remaining;
|
||||
}
|
||||
|
||||
void stdio_init(void)
|
||||
{
|
||||
#if MODULE_VFS
|
||||
vfs_bind_stdio();
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t stdio_read(void* buffer, size_t count)
|
||||
{
|
||||
if (STDIO_SEMIHOSTING_RX) {
|
||||
xtimer_ticks32_t last_wakeup = xtimer_now();
|
||||
ssize_t bytes_read = _semihosting_read(buffer, count);
|
||||
while (bytes_read == 0) {
|
||||
xtimer_periodic_wakeup(&last_wakeup, STDIO_SEMIHOSTING_POLL_RATE);
|
||||
bytes_read = _semihosting_read(buffer, count);
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ssize_t stdio_write(const void* buffer, size_t len)
|
||||
{
|
||||
if (!_semihosting_connected()) {
|
||||
return len;
|
||||
}
|
||||
size_t remaining = _semihosting_write(buffer, len);
|
||||
return len - remaining;
|
||||
}
|
Loading…
Reference in New Issue
Block a user