FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

RISC-VのSupervisor Binary Interface(SBI)について

RISC-Vのハイパーバイザについて調べているとき登場したが、Supervisor Binary Interface(SBI)というものが定義されている。

f:id:msyksphinz:20181030225822p:plain

詳細はRISC-V SBI specificationに説明がされている。

SBIというのはスーパバイザ実行環境(Supervisor Execution Environment)で実行できる機能群を示している。 SBIはBerkeley Boot Loader(BBL)上で実装されており、以下のSBIコールが定義されている。

Type Function Function ID
Timer sbi_set_timer 0
IPI sbi_clear_ipi
sbi_send_ipi
3
4
Memory Model sbi_remote_fence_i
sbi_remote_sfence_vma
sbi_remote_sfence_vma_asid
5
6
7
Console sbi_console_putchar
sbi_console_getchar
1
2
Shutdown sbi_shutdown 8

ここからは、それぞれのSBIコールの中身を、riscv-pk/machine/mtrap.cから観測していく。

タイマー

void sbi_set_timer(uint64_t stime_value)
  • mtrap.c
void mcall_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
{
...
    case SBI_SET_TIMER:
#if __riscv_xlen == 32
      retval = mcall_set_timer(arg0 + ((uint64_t)arg1 << 32));
#else
      retval = mcall_set_timer(arg0);
#endif
      break;
...
}

static uintptr_t mcall_set_timer(uint64_t when)
{
  *HLS()->timecmp = when;
  clear_csr(mip, MIP_STIP);
  set_csr(mie, MIP_MTIP);
  return 0;
}

IPI (Inter processor Interrupt)

他のプロセッサへの割り込み処理は、HLSというメモリ空間を使用して実行するらしい。

void sbi_send_ipi(const unsigned long *hart_mask)
  • mtrap.c
void mcall_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
{
...
    case SBI_SEND_IPI:
      ipi_type = IPI_SOFT;
      goto send_ipi;
...
send_ipi:
      send_ipi_many((uintptr_t*)arg0, ipi_type);
...
}

static void send_ipi(uintptr_t recipient, int event)
{
  if (((disabled_hart_mask >> recipient) & 1)) return;
  atomic_or(&OTHER_HLS(recipient)->mipi_pending, event);
  mb();
  *OTHER_HLS(recipient)->ipi = 1;
}
  • mtrap.h
// hart-local storage, at top of stack
#define HLS() ((hls_t*)(MACHINE_STACK_TOP() - HLS_SIZE))
#define OTHER_HLS(id) ((hls_t*)((void*)HLS() + RISCV_PGSIZE * ((id) - read_const_csr(mhartid))))

メモリモデル

sfenceによるメモリのバリアも、マルチコアで同期をとる必要があるためIPIを使って割り込みを出している。

  • mtrap.c
void sbi_remote_fence_i(const unsigned long *hart_mask)
void sbi_remote_sfence_vma(const unsigned long *hart_mask,
                           unsigned long start,
                           unsigned long size)
void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
                                unsigned long start,
                                unsigned long size,
                                unsigned long asid)

コンソール

putchar()getchar()である。これは普通にメモリストアして文字を書き出したりメモリリードして文字を読み取る。

static uintptr_t mcall_console_putchar(uint8_t ch)
{
  if (uart) {
    uart_putchar(ch);
  } else if (htif) {
    htif_console_putchar(ch);
  }
  return 0;
}

static uintptr_t mcall_console_getchar()
{
  if (uart) {
    return uart_getchar();
  } else if (htif) {
    return htif_console_getchar();
  } else {
    return '\0';
  }
}

Shutdown

シャットダウン用のSBIも定義されている。これも単純にfinisherとして定義されたメモリ位置を操作するだけである。

void poweroff(uint16_t code)
{
  printm("Power off\n");
  finisher_exit(code);
  if (htif) {
    htif_poweroff();
  } else {
    while (1);
  }
}

void finisher_exit(uint16_t code)
{
  if (!finisher) return;
  if (code == 0) {
    *finisher = FINISHER_PASS;
  } else {
    *finisher = code << 16 | FINISHER_FAIL;
  }
}