Page 1 of 2
Virtual control interface via semihosting
Posted: Fri May 10, 2013 1:29 pm
by inca
Greetings ChibiOSers,
Has anyone put any thought into turning the semihosting features of the gcc compiler into a virtual network or serial interface?
Page 27:
http://www2.lauterbach.com/pdf/debugger_arm.pdfIt appears ARM may already have some some capabilities along these lines, such as the Virtual Terminal.
---I have seen the implementations of semihosting for target->host communication, now I seek an example of host->target semihosting---
Code: Select all
#include <stdio.h>
int main()
{
char str[80];
while (1) {
printf("Hello! What say, ye? ");
scanf("%s", str);
puts(str);
}
return 0;
}
Cheers.
Re: Virtual control interface via semihosting
Posted: Fri May 10, 2013 1:47 pm
by Giovanni
Hi,
Hiding it behind a serial-like interface would be easily done if you can find some code to wrap.
Giovanni
Re: Virtual control interface via semihosting
Posted: Fri May 10, 2013 3:04 pm
by inca
So far, it appears stdio just works.
Code: Select all
#include <stdio.h>
int main()
{
char str[80];
int i, isquared;
FILE *fp;
fp = fopen("hello.txt", "r+");
if (fp == NULL) {
printf("Could not open hello.txt.\n");
return 0;
}
/* write to the file */
for (i=0; i<=10; ++i)
fprintf(fp, "%d, %d\n", i, i*i);
/* read the file */
fseek (fp, 0, SEEK_SET);
while (fscanf(fp, "%d,%d\n", &i, &isquared) == 2)
printf("i: %d, isquared: %d\n", i, isquared);
/* Interact */
str[0] = '\0';
while (str[0] != '0') {
printf("Hello! What say, ye? (0 to exit) ");
scanf("%s", str);
fprintf(fp, "%s\n", str);
printf("%s\n", str);
//fflush(fp);
}
/* close the file */
fclose(fp);
return 0;
}
#ifndef __NO_SYSTEM_INIT
void SystemInit()
{}
#endif
EDIT: This code reads and writes, as well as stubs SystemInit(), if necessary.
Re: Virtual control interface via semihosting
Posted: Fri May 10, 2013 3:51 pm
by inca
My development details:
OpenOCD 0.7 (0.8 dev) -> STLink/V2 (commercial variant: STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748) -> STM32F107 custom board.
OpenOCD launch command:
Code: Select all
openocd -f interface/stlink-v2.cfg -f target/stm32f1x_stlink.cfg
GNU-ARM-Embedded gdb .gdbinit file and launch command, NOTE: "monitor arm semihosting enable":
Code: Select all
gdbinit:
set mem inaccessible-by-default off
set confirm off
set target-async on
launch command:
arm-none-eabi-gdb -ex "target ext localhost:3333" -ex "mon reset halt" -ex "mon arm semihosting enable"
gdb commands:
load
run
switch to openocd terminal to see the stdout/stdin streams.
The makefiles and such came from G-A-E's semihosting example in
gcc-arm-none-eabi/share/gcc-arm-none-eabi/samples:
MakefileCode: Select all
include makefile.conf
NAME=semihost
STARTUP_DEFS=
LDSCRIPTS=-L. -L$(BASE)/ldscripts -T gcc.ld
LFLAGS=$(USE_NANO) $(USE_SEMIHOST) $(LDSCRIPTS) $(GC) $(MAP) -g3 -std=gnu99
$(NAME)-$(CORE).axf: $(NAME).c $(STARTUP)
$(CC) $^ $(CFLAGS) $(LFLAGS) -o $@
clean:
rm -f $(NAME)*.axf *.map
makefile.confCode: Select all
# Selecting Core
CORTEX_M=3
# Use newlib-nano. To disable it, specify USE_NANO=
USE_NANO=--specs=nano.specs
# Use seimhosting or not
USE_SEMIHOST=--specs=rdimon.specs -lc -lc -lrdimon
USE_NOHOST=-lc -lc -lnosys
CORE=CM$(CORTEX_M)
BASE=./
# Compiler & Linker
CC=arm-none-eabi-gcc
CXX=arm-none-eabi-g++
# Options for specific architecture
ARCH_FLAGS=-mthumb -mcpu=cortex-m$(CORTEX_M)
# Startup code
STARTUP=$(BASE)/startup/startup_ARM$(CORE).S
# -Os -ffunction-sections -fdata-sections to compile for code size
CFLAGS=$(ARCH_FLAGS) $(STARTUP_DEFS) -Os -ffunction-sections -fdata-sections -g3 -fasynchronous-unwind-tables
#-funwind-tables
# Link for code size
GC=-Wl,--gc-sections
# Create map file
MAP=-Wl,-Map=$(NAME).map
ldscripts (mem.ld and sections.ld):
Code: Select all
/* Linker script to configure memory regions.
* Need modifying for a specific board.
* FLASH.ORIGIN: starting address of flash
* FLASH.LENGTH: length of flash
* RAM.ORIGIN: starting address of RAM bank 0
* RAM.LENGTH: length of RAM bank 0
*/
/* STM32F107VCT6, 256K flash, 64K RAM. */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x40000 /* 256k */ /* 0x20000 128K */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000 /* 64k */ /* 0x2000 8K */
}
/* Linker script to configure memory regions.
* Need modifying for a specific board.
* FLASH.ORIGIN: starting address of flash
* FLASH.LENGTH: length of flash
* RAM.ORIGIN: starting address of RAM bank 0
* RAM.LENGTH: length of RAM bank 0
*/
/* STM32F107VCT6, 256K flash, 64K RAM. */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x40000 /* 256k */ /* 0x20000 128K */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000 /* 64k */ /* 0x2000 8K */
}
/* Linker script to place sections and symbol values. Should be used together
* with other linker script that defines memory regions FLASH and RAM.
* It references following symbols, which must be defined in code:
* Reset_Handler : Entry of reset handler
*
* It defines following symbols, which code can use without definition:
* __exidx_start
* __exidx_end
* __etext
* __data_start__
* __preinit_array_start
* __preinit_array_end
* __init_array_start
* __init_array_end
* __fini_array_start
* __fini_array_end
* __data_end__
* __bss_start__
* __bss_end__
* __end__
* end
* __HeapLimit
* __StackLimit
* __StackTop
* __stack
*/
ENTRY(Reset_Handler)
SECTIONS
{
.text :
{
KEEP(*(.isr_vector))
*(.text*)
KEEP(*(.init))
KEEP(*(.fini))
/* .ctors */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* .dtors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.rodata*)
KEEP(*(.eh_frame*))
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
__etext = .;
.data : AT (__etext)
{
__data_start__ = .;
*(vtable)
*(.data*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
KEEP(*(.jcr*))
. = ALIGN(4);
/* All data end */
__data_end__ = .;
} > RAM
.bss :
{
. = ALIGN(4);
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (COPY):
{
__end__ = .;
end = __end__;
*(.heap*)
__HeapLimit = .;
} > RAM
/* .stack_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later */
.stack_dummy (COPY):
{
*(.stack*)
} > RAM
/* Set stack top to end of RAM, and stack limit move down by
* size of stack_dummy section */
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
}
gdb output:
Reading symbols from /STM32/devtools/link_devices/semihosting-test/semihost-CM3.axf...done.
0x00000000 in ?? ()
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000220 msp: 0x20010000
semihosting is enabled
(gdb) load
Loading section .text, size 0x38dc lma 0x8000000
Loading section .ARM.extab, size 0x30 lma 0x80038dc
Loading section .ARM.exidx, size 0xe0 lma 0x800390c
Loading section .data, size 0x188 lma 0x80039ec
Start address 0x8000221, load size 15220
Transfer rate: 12 KB/sec, 3805 bytes/write.
(gdb) run
^C^C(gdb) quit
OpenOCD stdout:
Open On-Chip Debugger 0.8.0-dev-00003-g2a864a8-dirty (2013-05-06-19:35)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.sourceforge.net/doc/doxygen/bugs.htmlInfo : This adapter doesn't support configurable speed
Info : STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : Target voltage: 3.224021
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : accepting 'gdb' connection from 3333
Info : device id = 0x10016418
Info : flash size = 256kbytes
Warn : acknowledgment received, but no packet pending
undefined debug reason 6 - target needs reset
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000220 msp: 0x20010000
semihosting is enabled
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000220 msp: 0x20010000, semihosting
i: 0, isquared: 0
i: 1, isquared: 1
i: 2, isquared: 4
i: 3, isquared: 9
i: 4, isquared: 16
i: 5, isquared: 25
i: 6, isquared: 36
i: 7, isquared: 49
i: 8, isquared: 64
i: 9, isquared: 81
i: 10, isquared: 100
Hello! What say, ye? (0 to exit) my name is inca
my
Hello! What say, ye? (0 to exit) name
Hello! What say, ye? (0 to exit) is
Hello! What say, ye? (0 to exit) inca
Hello! What say, ye? (0 to exit) 0
0
semihosting: *** application exited ***
Info : The target is not running when halt was requested, stopping GDB.
Info : The target is not running when halt was requested, stopping GDB.
Info : The target is not running when halt was requested, stopping GDB.
Info : The target is not running when halt was requested, stopping GDB.
Info : dropped 'gdb' connection
^C
# cat hello.txt
0, 0
1, 1
2, 4
3, 9
4, 16
5, 25
6, 36
7, 49
8, 64
9, 81
10, 100
my
name
is
inca
0
Re: Virtual control interface via semihosting
Posted: Fri May 10, 2013 7:42 pm
by inca
I have come across some trouble integrating a working version of this configuration into ChibiOS. It seems like _malloc_r () is getting a bad 0x55555555 value somehow. I've tried enabling the chconf heap stuff but nothing works. I see that the definition of _sbrk is different in syscalls.c than the ARM one. Perhaps this is the cause?
Re: Virtual control interface via semihosting
Posted: Fri May 10, 2013 7:47 pm
by Giovanni
It is possible, the ChibiOS example syscall.c does not allow for negative values in sbrk(), probably this is what is happening. 0x55 is the default stack filler value. You could try implementing a sbrk() into a static memory area.
Giovanni
Re: Virtual control interface via semihosting
Posted: Sat May 11, 2013 12:42 am
by inca
I am completely stuck. I have no idea why _sbrk_r is getting garbage values from _malloc.
The first _sbrk_r is called from malloc+78 (+4E), it sees this:
_sbrk_r (r=0x55555555, incr=1431655765) at ../../os/various/syscalls.c:142
just before _sbrk_r is called from malloc+78:
r1 (incr) <= r5
r0 (r) <= r6
r6 is set by r0 (first argument) of the malloc_r call.
r5 is bit more complicated:
Code: Select all
.text:08001FD0 EXPORT _malloc_r
.text:08001FD0 _malloc_r ; CODE XREF: __sfmoreglue+Cp
.text:08001FD0 ; __smakebuf_r+52p
.text:08001FD0 PUSH {R4-R6,LR}
.text:08001FD2 ADDS R5, R1, #3
.text:08001FD4 BIC.W R5, R5, #3
.text:08001FD8 ADDS R5, #8
.text:08001FDA CMP R5, #0xC
.text:08001FDC IT CC
.text:08001FDE MOVCC R5, #0xC
.text:08001FE0 CMP R5, #0
.text:08001FE2 MOV R6, R0
.text:08001FE4 BLT loc_8002042
I don't know what this does yet.
_malloc_r may be called by either __sfmoreglue+0xC or __smakebuf_r+0x52. In this case, it is only called by __sfmoreglue:
Code: Select all
.text:08002150
.text:08002150 EXPORT __sfmoreglue
.text:08002150 __sfmoreglue ; CODE XREF: __sfp+32p
.text:08002150 PUSH {R4-R6,LR}
.text:08002152 MOVS R5, #0x68
.text:08002154 MULS R5, R1
.text:08002156 MOV R6, R1
.text:08002158 ADD.W R1, R5, #0xC
.text:0800215C CBZ R0, loc_8002176
which is called from one place in __sfp (+0x32)
I have downloaded g-a-e source to build the symbols in and browse the various newlib codes. However, it would be nice if we could use semihosting with a bit more ease in ChibiOS.
Cheers.
Re: Virtual control interface via semihosting
Posted: Fri May 17, 2013 6:04 am
by inca
G et al,
I doubt this is any fault of ChibiOS. I checked the syscall.c _sbrk_r but I did not see it get passed any negative values.
Compiling with -O0 is the only way for me to get the chprintf() line to work in the code below. The raw and chSequentialStreamWrite macro works just fine under all the cases I've tested so far. I wonder how hard it will be to get receive side to work under these conditions.
Unfortunately, I cannot seem to get the newlib or newlib-nano stuff from GCC-ARM-Embedded to play along with ChibiOS. I could not seem to find the relevant differences to isolate the issue because of the significan differences (such as syscalls.c vs the default ARM code). I have no idea how necessary it is, though I am sure some people will want it just for convenience. After debugging malloc and getting through 1500 instructions or so to get to the fault which kills gdb/openocd each time, I, frankly, could not care less whether or not I ever see those implementations again. 1500 thumb-2 instructions to move 17 characters into a buffer. It seems to me that there may be a few extra kitchen sinks in that flying nuclear fortress that is newlib.
The code:
Code: Select all
#include <stdio.h>
#include "ch.h"
#include "hal.h"
#include "chprintf.h"
// Create semihost BaseChannel struct pointing to our semihosting Virtual Methods Table (vmt)
BaseChannel semihostBC;
BaseChannel *chp = &semihostBC;
// writes for semihost: (void *instance, const uint8_t *bp, size_t n)
static size_t writes(BaseChannel *stream, const char *ptr, size_t size)
{
(void)stream;
static uint32_t args[3];
args[0] = 1;
args[1] = (uint32_t)ptr;
args[2] = size;
asm("mov r0, #5\n"
"mov r1, %0\n"
"bkpt 0x00ab" : : "r"(args) : "r0", "r1");
return args[2];
}
// Stolen from boiler plate BaseChannel methods from chioch.h
// This putt is an ugly hack. TODO: a line buffer up to length n.
static msg_t putt(void *ip, uint8_t b, systime_t time) {
(void)time;
return writes(ip, (char*) &b, 1);
}
//
static msg_t puttc(void *ip, uint8_t b, systime_t time) {
(void)time;
(void)ip;
fputc(b, stdout);
fflush(stdout);
return RDY_OK;
}
// Instantiate BaseSequentialStreamVMT with #define _base_channel_methods
//static const struct BaseSequentialStreamVMT vmt = {
static const struct BaseChannelVMT vmt = {
(void*)writes, // size_t (*write)(void *instance, const uint8_t *bp, size_t n)
NULL, // size_t (*read)(void *instance, uint8_t *bp, size_t n);
NULL, NULL, // would blocks
(void*)putt, NULL, // put/get
NULL, NULL // writet/readt
};
int main(void) {
uint8_t str1[] = "Raw DeadSemiBeef!\n";
uint8_t str2[] = "chSequentialStreamWrite Foobar!\n";
halInit();
chSysInit();
chThdSleepMilliseconds(500);
semihostBC.vmt = &vmt;
/* Raw DeadSemiBeef!
* Works in all tested configurations as the raw bkpt func.
*/
static uint32_t args[3];
args[0] = 1;
args[1] = (uint32_t)str1;
args[2] = sizeof(str1)-1;
asm("mov r0, #5\n"
"mov r1, %0\n"
"bkpt 0x00ab" : : "r"(args) : "r0", "r1");
/* chSequentialStreamWrite Foobar!
* Works in all tested configurations as the mapped accessor to the bkpt func.
*/
chSequentialStreamWrite(chp, str2, sizeof(str2)-1);
/* chprintf Foobar!
* Prints very slowly with putt and not at all with puttc in VMT.
* The puttc (newlib-nano) should be one way to satisfy the put requirement
* for chprintf().
* Works in -O0, not -O2
*/
chprintf(chp, "chprintf Foobar!\n");
/* printf Footbar!
* Prints in GCC-ARM-Embedded examples, not in ChibiOS.
* Occasionally hardfaults with other -Ox optimizations than -O0.
*/
printf("printf Footbar!\n");
return 0;
}
Re: Virtual control interface via semihosting
Posted: Tue Feb 11, 2014 6:49 am
by DeusExMachina
inca
What is the final result of your work?
Re: Virtual control interface via semihosting
Posted: Wed Apr 29, 2015 9:20 pm
by inca
DeusExMachina,
IIRC, my findings were that semihosting is slow and painful. That could have changed in the meanwhile.