FPGA開発日記

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

RISC-Vの割り込みとタイマの仕組み

RISC-Vの割り込みについて。

RISC-Vのサポートしている割り込み・例外について

こちらについては、一覧表が作られているので参照されたい。

f:id:msyksphinz:20180808230058p:plain
図. RISC-Vのサポートしている割り込み・例外。(The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture Version 1.10 より抜粋)

上記の値が要因の値として、例外・割り込み発生時には例外要因レジスタ(mcause, scausenなど)に格納される。

タイマ割り込みの挿入について

例えば、SiFiveのSoCプラットフォームであれば、タイマ割り込み向けのレジスタはCPUの外に定義されている。 CLINTレジスタ群がそれに当たる。0x0200_0000に定義されている(これはSiFiveのプラットフォームではこのように定義されている、という話であってすべてがそうではない)。

f:id:msyksphinz:20180808231519p:plain

RISC-Vの仕様としては、mtimeシステムレジスタを参照すればよいのだが、SiFiveではmtimeレジスタを持っていないので、システムレジスタにアクセスした際に例外が発生し、上記のCLINT Memory Mappedレジスタにアクセスするようになっている。

上記の資料では、mtimecmpレジスタが5つ定義されているが、U54-MCではCPUコアが5つ搭載されているのでそれぞれのコア向けに定義されている。このmtimecmpと比較して、一致すればそのコアに割り込みが発生するわけだ。

では、割り込みが発生したときに、CPUが割り込み処理にジャンプする条件はどのようになっているのだろうか?ここでは、一番簡単にCPUモデルの記述を見てチェックしていこう。 Spikeシミュレータの実装を見るのが簡単だ。

  • riscv-isa-sim/riscv/clint.cc
void clint_t::increment(reg_t inc)
{
  mtime += inc;
  for (size_t i = 0; i < procs.size(); i++) {
    procs[i]->state.mip &= ~MIP_MTIP;
    if (mtime >= mtimecmp[i])
      procs[i]->state.mip |= MIP_MTIP;
  }
}

明らかに実装を見るとわかる。割り込みを発生させるとCPU内のMIPレジスタに1が設定され、これが割り込み発生トリガとなる。では、これを受け付ける条件は何か。

結論から言うと、割り込み許可レジスタ(MIE)がAssertされているときである。

  • riscv-isa-sim/riscv/processor.h
  void take_pending_interrupt() { take_interrupt(state.mip & state.mie); }

割り込み受付処理

では、割り込み受付条件を見てみよう。midelegレジスタは割り込み受付条件を譲渡するためのシステムレジスタで、midelegの該当ビットが1になっていれば、その割り込み要因はそのままSupervisorモードに渡される。

  • riscv-isa-sim/riscv/processor.cc
void processor_t::take_interrupt(reg_t pending_interrupts)
{
  reg_t mie = get_field(state.mstatus, MSTATUS_MIE);
  reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie);
  reg_t enabled_interrupts = pending_interrupts & ~state.mideleg & -m_enabled;

  reg_t sie = get_field(state.mstatus, MSTATUS_SIE);
  reg_t s_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie);
  // M-ints have highest priority; consider S-ints only if no M-ints pending
  if (enabled_interrupts == 0)
    enabled_interrupts = pending_interrupts & state.mideleg & -s_enabled;

...
}

割り込みの受付優先度

The RISC-V Instruction Set Manual Volume II: Privileged Architecture Privileged Architecture Version 1.10 によると、

The priority value 0 is reserved to mean \never interrupt", and interrupt priority increases with increasing integer values.

つまり、割り込み要因の数字が大きいほど割り込み優先度が高い。このルールに従って割り込みを受け付ける。

  • riscv-isa-sim/riscv/processor.cc
    // nonstandard interrupts have highest priority
    if (enabled_interrupts >> IRQ_M_EXT)
      enabled_interrupts = enabled_interrupts >> IRQ_M_EXT << IRQ_M_EXT;
    // external interrupts have next-highest priority
    else if (enabled_interrupts & (MIP_MEIP | MIP_SEIP))
      enabled_interrupts = enabled_interrupts & (MIP_MEIP | MIP_SEIP);
    // software interrupts have next-highest priority
    else if (enabled_interrupts & (MIP_MSIP | MIP_SSIP))
      enabled_interrupts = enabled_interrupts & (MIP_MSIP | MIP_SSIP);
    // timer interrupts have next-highest priority
    else if (enabled_interrupts & (MIP_MTIP | MIP_STIP))
      enabled_interrupts = enabled_interrupts & (MIP_MTIP | MIP_STIP);
    else
      abort();
  • riscv-isa-sim/riscv/sim.cc
// fetch/decode/execute loop
void processor_t::step(size_t n)
{
...
    try
    {
      take_pending_interrupt();

      if (unlikely(slow_path()))
      {
        while (instret < n)
        {
          if (unlikely(!state.serialized && state.single_step == state.STEP_STEPPED)) {
            state.single_step = state.STEP_NONE;