1
0
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:
Koen Zandberg 2020-02-09 12:52:20 +01:00
parent aff18b9599
commit 99ca736b53
No known key found for this signature in database
GPG Key ID: 0895A893E6D2985B
5 changed files with 223 additions and 1 deletions

View File

@ -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

View File

@ -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

View 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 */

View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View 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;
}