Virtual control interface via semihosting

ChibiOS public support forum for topics related to the STMicroelectronics STM32 family of micro-controllers.

Moderators: RoccoMarco, barthess

inca
Posts: 37
Joined: Mon Apr 22, 2013 12:08 am

Virtual control interface via semihosting

Postby inca » Fri May 10, 2013 1:29 pm

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.pdf

It 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.
Last edited by inca on Fri May 10, 2013 1:48 pm, edited 1 time in total.

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: Virtual control interface via semihosting

Postby Giovanni » Fri May 10, 2013 1:47 pm

Hi,

Hiding it behind a serial-like interface would be easily done if you can find some code to wrap.

Giovanni

inca
Posts: 37
Joined: Mon Apr 22, 2013 12:08 am

Re: Virtual control interface via semihosting

Postby inca » Fri May 10, 2013 3:04 pm

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.
Last edited by inca on Fri May 10, 2013 3:53 pm, edited 1 time in total.

inca
Posts: 37
Joined: Mon Apr 22, 2013 12:08 am

Re: Virtual control interface via semihosting

Postby inca » Fri May 10, 2013 3:51 pm

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:
Makefile

Code: 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.conf

Code: 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.html
Info : 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

inca
Posts: 37
Joined: Mon Apr 22, 2013 12:08 am

Re: Virtual control interface via semihosting

Postby inca » Fri May 10, 2013 7:42 pm

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?

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: Virtual control interface via semihosting

Postby Giovanni » Fri May 10, 2013 7:47 pm

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

inca
Posts: 37
Joined: Mon Apr 22, 2013 12:08 am

Re: Virtual control interface via semihosting

Postby inca » Sat May 11, 2013 12:42 am

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.

inca
Posts: 37
Joined: Mon Apr 22, 2013 12:08 am

Re: Virtual control interface via semihosting

Postby inca » Fri May 17, 2013 6:04 am

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

User avatar
DeusExMachina
Posts: 223
Joined: Tue Apr 03, 2012 5:08 am
Location: South Korea
Has thanked: 3 times
Been thanked: 3 times

Re: Virtual control interface via semihosting

Postby DeusExMachina » Tue Feb 11, 2014 6:49 am

inca
What is the final result of your work?

inca
Posts: 37
Joined: Mon Apr 22, 2013 12:08 am

Re: Virtual control interface via semihosting

Postby inca » Wed Apr 29, 2015 9:20 pm

DeusExMachina,

IIRC, my findings were that semihosting is slow and painful. That could have changed in the meanwhile.


Return to “STM32 Support”

Who is online

Users browsing this forum: No registered users and 28 guests