Semihosting on HiFive1 Board - using freedom-e-sdk

Hi Everyone,
I’m tring to add semi-hosting support to my application which i’m building with freedom-e-sdk. As many you already know that semi-hosting is already implemented in “micro-os-plus” but in my case i’m trying to run my application without any OS. To understand and use semi-hosting in my application which i’m building using freedom-e-sdk, i did the following

  1. downloaded and built latest openocd 8dd5d which supports riscv semi-hosting

  2. Modified led_fade program to send a string “Hello Semi” and print it openocd console.

  3. I have copied semi-hosting implementation from “micro-os-plus” RTOS

  4. used run_openocd and run_gdb targets to run the application on hifive1 board.

  5. Made changes in top level makefile to add -g3 flag and -Og.

CFLAGS=“-Og -march=$(RISCV_ARCH) -mabi=$(RISCV_ABI) -mcmodel=medany -g3 -I$(abspath $(MEE_BSP_PATH)/install/include/)”
CXXFLAGS=“-Og -march=$(RISCV_ARCH) -mabi=$(RISCV_ABI) -g3 -mcmodel=medany -I$(abspath $(MEE_BSP_PATH)/install/include/)” \

Below is the code

   // See LICENSE for license details.

// This is the program which ships on the HiFive1
// board, executing out of SPI Flash at 0x20400000.

#include <stdint.h>
#include "platform.h"
#define RISCV_SEMIHOSTING_CALL_NUMBER 7 //added
#define OS_INTEGER_TRACE_SEMIHOSTING_BUFF_ARRAY_SIZE  (16)

#ifndef _SIFIVE_HIFIVE1_H
#error "'led_fade' is designed to run on HiFive1 and/or E300 Arty Dev Kit."
#endif




 enum semihosting_operation_numbers
  {
/*
 * ARM semihosting operations, in lexicographic order.
 */
SEMIHOSTING_ENTER_SVC = 0x17, /* DEPRECATED */

SEMIHOSTING_SYS_CLOSE = 0x02,
SEMIHOSTING_SYS_CLOCK = 0x10,
SEMIHOSTING_SYS_ELAPSED = 0x30,
SEMIHOSTING_SYS_ERRNO = 0x13,
SEMIHOSTING_SYS_EXIT = 0x18,
SEMIHOSTING_SYS_EXIT_EXTENDED = 0x20,
SEMIHOSTING_SYS_FLEN = 0x0C,
SEMIHOSTING_SYS_GET_CMDLINE = 0x15,
SEMIHOSTING_SYS_HEAPINFO = 0x16,
SEMIHOSTING_SYS_ISERROR = 0x08,
SEMIHOSTING_SYS_ISTTY = 0x09,
SEMIHOSTING_SYS_OPEN = 0x01,
SEMIHOSTING_SYS_READ = 0x06,
SEMIHOSTING_SYS_READC = 0x07,
SEMIHOSTING_SYS_REMOVE = 0x0E,
SEMIHOSTING_SYS_RENAME = 0x0F,
SEMIHOSTING_SYS_SEEK = 0x0A,
SEMIHOSTING_SYS_SYSTEM = 0x12,
SEMIHOSTING_SYS_TICKFREQ = 0x31,
SEMIHOSTING_SYS_TIME = 0x11,
SEMIHOSTING_SYS_TMPNAM = 0x0D,
SEMIHOSTING_SYS_WRITE = 0x05,
SEMIHOSTING_SYS_WRITEC = 0x03,
SEMIHOSTING_SYS_WRITE0 = 0x04,
  };

static const char msg[] = "Hello Semi\r\n";




static inline int
  __attribute__ ((always_inline))
  os_semihosting_call_host (int reason, void* arg)
  {
register int value asm ("a0") = reason;
register void* ptr asm ("a1") = arg;

asm volatile (

    // Workaround for RISC-V lack of multiple EBREAKs.
    " .option push \n"
    " .option norvc \n"
    " slli x0, x0, 0x1f \n"
    " ebreak \n"
    " srai x0, x0, %[swi] \n"
    " .option pop \n"

    : "=r" (value) // Outputs 
    : "0" (value), "r" (ptr), [swi] "i" (RISCV_SEMIHOSTING_CALL_NUMBER) // Inputs 
    : "memory" // Clobbers 
);

return value;
}



int dwrite (const void* buf, int nbyte)   // Debug channel write.
{
 char tmp[OS_INTEGER_TRACE_SEMIHOSTING_BUFF_ARRAY_SIZE];
 int togo = nbyte;
  if (buf == NULL || nbyte == 0)
    {
      return 0;
    }

  const char* cbuf = (const char*) buf;

  // Since the single character debug channel is quite slow, try to
  // optimize and send a null terminated string, if possible.
  if (cbuf[nbyte] == '\0')
    {
      // send string
      os_semihosting_call_host(SEMIHOSTING_SYS_WRITE0, (void*) cbuf);
    }
  else
    {
      // If not, use a local buffer to speed things up.
      // For re-entrance, this bugger must be allocated on the stack,
      // so be cautious with the size.

      while (togo > 0)
        {
          int n = ((togo < sizeof(tmp)) ? togo : sizeof(tmp) - 1);
          int i = 0;
          for (; i < n; ++i, ++cbuf)
            {
              tmp[i] = *cbuf;
            }
          tmp[i] = '\0';

          os_semihosting_call_host (SEMIHOSTING_SYS_WRITE0, (void*) tmp);

          togo -= n;
        }
    }

  // All bytes written.
  return (int) nbyte;
}


int main (void){



  // Make sure the HFROSC is on before the next line:
  PRCI_REG(PRCI_HFROSCCFG) |= ROSC_EN(1);
  // Run off 16 MHz Crystal for accuracy. Note that the
  // first line is
  PRCI_REG(PRCI_PLLCFG) = (PLL_REFSEL(1) | PLL_BYPASS(1));
  PRCI_REG(PRCI_PLLCFG) |= (PLL_SEL(1));
  // Turn off HFROSC to save power
  PRCI_REG(PRCI_HFROSCCFG) &= ~(ROSC_EN(1));


  uint16_t r=0xFF;
  uint16_t g=0;
  uint16_t b=0;
  char c = 0;

  // Set up RGB PWM

  PWM1_REG(PWM_CFG)   = 0;
  // To balance the power consumption, make one left, one right, and one center aligned.
  PWM1_REG(PWM_CFG)   = (PWM_CFG_ENALWAYS) | (PWM_CFG_CMP2CENTER);
  PWM1_REG(PWM_COUNT) = 0;

  // Period is approximately 244 Hz
  // the LEDs are intentionally left somewhat dim,
  // as the full brightness can be painful to look at.
  PWM1_REG(PWM_CMP0)  = 0;

  GPIO_REG(GPIO_IOF_SEL)    |= ( (1 << GREEN_LED_OFFSET) | (1 << BLUE_LED_OFFSET) | (1 << RED_LED_OFFSET));
  GPIO_REG(GPIO_IOF_EN )    |= ( (1 << GREEN_LED_OFFSET) | (1 << BLUE_LED_OFFSET) | (1 << RED_LED_OFFSET));
  GPIO_REG(GPIO_OUTPUT_XOR) &= ~( (1 << GREEN_LED_OFFSET) | (1 << BLUE_LED_OFFSET));
  GPIO_REG(GPIO_OUTPUT_XOR) |= (1 << RED_LED_OFFSET);

  dwrite(msg,sizeof(msg)-1); // send a string to print on debug channel

  while(1){
volatile uint64_t *  now = (volatile uint64_t*)(CLINT_CTRL_ADDR + CLINT_MTIME);
volatile uint64_t then = *now + 100;
while (*now < then) { }

if(r > 0 && b == 0){
  r--;
  g++;
}
if(g > 0 && r == 0){
  g--;
  b++;
}
if(b > 0 && g == 0){
  r++;
  b--;
}

uint32_t G = g;
uint32_t R = r;
uint32_t B = b;

PWM1_REG(PWM_CMP1)  = G << 4;            // PWM is low on the left, GPIO is low on the left side, LED is ON on the left.
PWM1_REG(PWM_CMP2)  = (B << 1) << 4;     // PWM is high on the middle, GPIO is low in the middle, LED is ON in the middle.
PWM1_REG(PWM_CMP3)  = 0xFFFF - (R << 4); // PWM is low on the left, GPIO is low on the right, LED is on on the right.

  }
}

the output on openocd console is:

Info : Found flash device ‘issi is25lp128’ (ID 0x0018609d)
Info : JTAG tap: riscv.cpu tap/device found: 0x10e31913 (mfg: 0x489 (SiFive, Inc.), part: 0x0e31, ver: 0x1)
halted at 0x20401090 due to hardware trigger
halted at 0x20401094 due to step

the output on gdb console when issue run command:

Reading symbols from software/semihost//semihost…
Remote debugging using localhost:3333
0x20401090 in ?? ()
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /media/cdac/OBU/hifive1/freedom-e-sdk/software/semihost/semihost
halted at 0x20401094 due to step

There is no output on either of the (gdb and openocd) consoles.

It is from my understanding that adding semi-hosting syscall (taken from micro-os-plus) in my application would be sufficient to print string on openocd console (since openocd i’m using has already semi-hosting support).

so the code flow would be dwrite->os_semihosting_call_host->trap_entry(entry.S)->handle_trap(init.c) ; from here debugger (openocd) gets notified about semi-hosting request and handles it. please correct me if i’m wrong.

could any help with how to make it work or where did i go wrong?

Thanks in advance.

openocd supports semihosting, but you need to explicitly enable it.

one clarification: µOS++ is not a RTOS in the traditional meaning, it is a framework which includes many components, including a RTOS. the µOS++ RTOS itself has a generic C++ API, which can run on top of its own scheduler, or on top of another scheduler, for example FreeRTOS.

yes enabling semi-hosting in openocd configuration solved the problem. Thanks a lot