mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
173 lines
4.3 KiB
C
173 lines
4.3 KiB
C
/*
|
|
* 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 and RISC-V Semihosting implementation
|
|
*
|
|
* RISC-V semihosting closely mimics ARM semihosting. Only the break sequence is
|
|
* different, but all defined values are also used with RISC-V
|
|
*
|
|
* @author Koen Zandberg <koen@bergzand.net>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include "stdio_semihosting.h"
|
|
#include "ztimer.h"
|
|
|
|
/**
|
|
* @brief Rate at which the stdin read polls (breaks) the debugger for input
|
|
* data in milliseconds
|
|
*/
|
|
#define STDIO_SEMIHOSTING_POLL_RATE_MS (10)
|
|
|
|
/**
|
|
* @brief ARM Semihosting STDIN file descriptor. Also used with RISC-V
|
|
*/
|
|
#define STDIO_SEMIHOSTING_F_STDIN (1)
|
|
|
|
/**
|
|
* @brief ARM Semihosting STDOUT file descriptor. Also used with RISC-V
|
|
*/
|
|
#define STDIO_SEMIHOSTING_F_STDOUT (1)
|
|
|
|
/**
|
|
* @name ARM Semihosting commands.
|
|
*
|
|
* RISC-V copied over these command names and values
|
|
*
|
|
* Extend when required
|
|
* @{
|
|
*/
|
|
#define STDIO_SEMIHOSTING_SYS_WRITE (0x05) /**< Write command */
|
|
#define STDIO_SEMIHOSTING_SYS_READ (0x06) /**< Read command */
|
|
/** @} */
|
|
|
|
#if defined(MODULE_RISCV_COMMON)
|
|
static bool _semihosting_connected(void) {
|
|
return true;
|
|
}
|
|
|
|
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__(
|
|
".option norvc \n"
|
|
"mv a0, %[cmd] \n"
|
|
"mv a1, %[args] \n"
|
|
/* Wrapping the ebreak instruction in two NOP SLLI and SRAI instructions
|
|
* act as indicator to the GDB session that this is a
|
|
* semihosting trap */
|
|
"slli x0, x0, 0x1f \n"
|
|
"ebreak \n"
|
|
"srai x0, x0, 7 \n"
|
|
"mv %[result], a0 \n"
|
|
: /* Outputs */
|
|
[result] "=r" (result)
|
|
: /* Inputs */
|
|
[cmd] "r" (cmd),
|
|
[args] "r" (args)
|
|
: /* Clobbered registers */
|
|
"a0", "a1", "memory"
|
|
);
|
|
return result;
|
|
}
|
|
|
|
#elif defined(MODULE_CORTEXM_COMMON)
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
}
|
|
|
|
ssize_t stdio_read(void* buffer, size_t count)
|
|
{
|
|
if (STDIO_SEMIHOSTING_RX) {
|
|
uint32_t last_wakeup = ztimer_now(ZTIMER_MSEC);
|
|
ssize_t bytes_read = _semihosting_read(buffer, count);
|
|
while (bytes_read == 0) {
|
|
ztimer_periodic_wakeup(ZTIMER_MSEC, &last_wakeup,
|
|
STDIO_SEMIHOSTING_POLL_RATE_MS);
|
|
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;
|
|
}
|